|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 sw=2 et tw=78: */ |
|
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 /* |
|
8 * Base class for all our document implementations. |
|
9 */ |
|
10 |
|
11 #include "nsDocument.h" |
|
12 |
|
13 #include "mozilla/ArrayUtils.h" |
|
14 #include "mozilla/AutoRestore.h" |
|
15 #include "mozilla/DebugOnly.h" |
|
16 #include "mozilla/MemoryReporting.h" |
|
17 #include "mozilla/Likely.h" |
|
18 #include <algorithm> |
|
19 |
|
20 #ifdef MOZ_LOGGING |
|
21 // so we can get logging even in release builds |
|
22 #define FORCE_PR_LOG 1 |
|
23 #endif |
|
24 #include "prlog.h" |
|
25 #include "plstr.h" |
|
26 #include "prprf.h" |
|
27 |
|
28 #include "mozilla/Telemetry.h" |
|
29 #include "nsIInterfaceRequestor.h" |
|
30 #include "nsIInterfaceRequestorUtils.h" |
|
31 #include "nsUnicharUtils.h" |
|
32 #include "nsContentList.h" |
|
33 #include "nsIObserver.h" |
|
34 #include "nsIBaseWindow.h" |
|
35 #include "mozilla/css/Loader.h" |
|
36 #include "mozilla/css/ImageLoader.h" |
|
37 #include "nsDocShell.h" |
|
38 #include "nsIDocShellTreeItem.h" |
|
39 #include "nsCOMArray.h" |
|
40 #include "nsDOMClassInfo.h" |
|
41 #include "nsCxPusher.h" |
|
42 |
|
43 #include "mozilla/AsyncEventDispatcher.h" |
|
44 #include "mozilla/BasicEvents.h" |
|
45 #include "mozilla/EventListenerManager.h" |
|
46 #include "mozilla/EventStateManager.h" |
|
47 #include "nsIDOMNodeFilter.h" |
|
48 |
|
49 #include "nsIDOMStyleSheet.h" |
|
50 #include "mozilla/dom/Attr.h" |
|
51 #include "nsIDOMDOMImplementation.h" |
|
52 #include "nsIDOMDocumentXBL.h" |
|
53 #include "mozilla/dom/Element.h" |
|
54 #include "nsGenericHTMLElement.h" |
|
55 #include "mozilla/dom/CDATASection.h" |
|
56 #include "mozilla/dom/ProcessingInstruction.h" |
|
57 #include "nsDOMString.h" |
|
58 #include "nsNodeUtils.h" |
|
59 #include "nsLayoutUtils.h" // for GetFrameForPoint |
|
60 #include "nsIFrame.h" |
|
61 #include "nsITabChild.h" |
|
62 |
|
63 #include "nsRange.h" |
|
64 #include "nsIDOMText.h" |
|
65 #include "nsIDOMComment.h" |
|
66 #include "mozilla/dom/DocumentType.h" |
|
67 #include "mozilla/dom/NodeIterator.h" |
|
68 #include "mozilla/dom/TreeWalker.h" |
|
69 |
|
70 #include "nsIServiceManager.h" |
|
71 |
|
72 #include "nsContentCID.h" |
|
73 #include "nsError.h" |
|
74 #include "nsPresShell.h" |
|
75 #include "nsPresContext.h" |
|
76 #include "nsIJSON.h" |
|
77 #include "nsThreadUtils.h" |
|
78 #include "nsNodeInfoManager.h" |
|
79 #include "nsIFileChannel.h" |
|
80 #include "nsIMultiPartChannel.h" |
|
81 #include "nsIRefreshURI.h" |
|
82 #include "nsIWebNavigation.h" |
|
83 #include "nsIScriptError.h" |
|
84 #include "nsStyleSheetService.h" |
|
85 |
|
86 #include "nsNetUtil.h" // for NS_MakeAbsoluteURI |
|
87 |
|
88 #include "nsIScriptSecurityManager.h" |
|
89 #include "nsIPrincipal.h" |
|
90 |
|
91 #include "nsIDOMWindow.h" |
|
92 #include "nsPIDOMWindow.h" |
|
93 #include "nsIDOMElement.h" |
|
94 #include "nsFocusManager.h" |
|
95 |
|
96 // for radio group stuff |
|
97 #include "nsIDOMHTMLInputElement.h" |
|
98 #include "nsIRadioVisitor.h" |
|
99 #include "nsIFormControl.h" |
|
100 |
|
101 #include "nsBidiUtils.h" |
|
102 |
|
103 #include "nsIDOMUserDataHandler.h" |
|
104 #include "nsIDOMXPathExpression.h" |
|
105 #include "nsIDOMXPathNSResolver.h" |
|
106 #include "nsIParserService.h" |
|
107 #include "nsContentCreatorFunctions.h" |
|
108 |
|
109 #include "nsIScriptContext.h" |
|
110 #include "nsBindingManager.h" |
|
111 #include "nsIDOMHTMLDocument.h" |
|
112 #include "nsHTMLDocument.h" |
|
113 #include "nsIDOMHTMLFormElement.h" |
|
114 #include "nsIRequest.h" |
|
115 #include "nsHostObjectProtocolHandler.h" |
|
116 |
|
117 #include "nsCharsetAlias.h" |
|
118 #include "nsCharsetSource.h" |
|
119 #include "nsIParser.h" |
|
120 #include "nsIContentSink.h" |
|
121 |
|
122 #include "nsDateTimeFormatCID.h" |
|
123 #include "nsIDateTimeFormat.h" |
|
124 #include "mozilla/EventDispatcher.h" |
|
125 #include "mozilla/EventStates.h" |
|
126 #include "mozilla/InternalMutationEvent.h" |
|
127 #include "nsDOMCID.h" |
|
128 |
|
129 #include "jsapi.h" |
|
130 #include "nsIXPConnect.h" |
|
131 #include "nsCCUncollectableMarker.h" |
|
132 #include "nsIContentPolicy.h" |
|
133 #include "nsContentPolicyUtils.h" |
|
134 #include "nsICategoryManager.h" |
|
135 #include "nsIDocumentLoaderFactory.h" |
|
136 #include "nsIDocumentLoader.h" |
|
137 #include "nsIContentViewer.h" |
|
138 #include "nsIXMLContentSink.h" |
|
139 #include "nsIXULDocument.h" |
|
140 #include "nsIPrompt.h" |
|
141 #include "nsIPropertyBag2.h" |
|
142 #include "nsIDOMPageTransitionEvent.h" |
|
143 #include "nsIDOMStyleRuleChangeEvent.h" |
|
144 #include "nsIDOMStyleSheetChangeEvent.h" |
|
145 #include "nsIDOMStyleSheetApplicableStateChangeEvent.h" |
|
146 #include "nsJSUtils.h" |
|
147 #include "nsFrameLoader.h" |
|
148 #include "nsEscape.h" |
|
149 #include "nsObjectLoadingContent.h" |
|
150 #include "nsHtml5TreeOpExecutor.h" |
|
151 #include "nsIDOMElementReplaceEvent.h" |
|
152 #include "mozilla/dom/HTMLLinkElement.h" |
|
153 #include "mozilla/dom/HTMLMediaElement.h" |
|
154 #ifdef MOZ_MEDIA_NAVIGATOR |
|
155 #include "mozilla/MediaManager.h" |
|
156 #endif // MOZ_MEDIA_NAVIGATOR |
|
157 #ifdef MOZ_WEBRTC |
|
158 #include "IPeerConnection.h" |
|
159 #endif // MOZ_WEBRTC |
|
160 |
|
161 #include "mozAutoDocUpdate.h" |
|
162 #include "nsGlobalWindow.h" |
|
163 #include "mozilla/dom/EncodingUtils.h" |
|
164 #include "mozilla/dom/quota/QuotaManager.h" |
|
165 #include "nsDOMNavigationTiming.h" |
|
166 |
|
167 #include "nsSMILAnimationController.h" |
|
168 #include "imgIContainer.h" |
|
169 #include "nsSVGUtils.h" |
|
170 #include "SVGElementFactory.h" |
|
171 |
|
172 #include "nsRefreshDriver.h" |
|
173 |
|
174 // FOR CSP (autogenerated by xpidl) |
|
175 #include "nsIContentSecurityPolicy.h" |
|
176 #include "nsCSPService.h" |
|
177 #include "nsHTMLStyleSheet.h" |
|
178 #include "nsHTMLCSSStyleSheet.h" |
|
179 #include "mozilla/dom/DOMImplementation.h" |
|
180 #include "mozilla/dom/ShadowRoot.h" |
|
181 #include "mozilla/dom/Comment.h" |
|
182 #include "nsTextNode.h" |
|
183 #include "mozilla/dom/Link.h" |
|
184 #include "mozilla/dom/HTMLElementBinding.h" |
|
185 #include "mozilla/dom/SVGElementBinding.h" |
|
186 #include "nsXULAppAPI.h" |
|
187 #include "mozilla/dom/Touch.h" |
|
188 #include "mozilla/dom/TouchEvent.h" |
|
189 #include "GeneratedEvents.h" |
|
190 |
|
191 #include "mozilla/Preferences.h" |
|
192 |
|
193 #include "imgILoader.h" |
|
194 #include "imgRequestProxy.h" |
|
195 #include "nsWrapperCacheInlines.h" |
|
196 #include "nsSandboxFlags.h" |
|
197 #include "nsIAppsService.h" |
|
198 #include "mozilla/dom/BindingUtils.h" |
|
199 #include "mozilla/dom/DocumentFragment.h" |
|
200 #include "mozilla/dom/Event.h" |
|
201 #include "mozilla/dom/HTMLBodyElement.h" |
|
202 #include "mozilla/dom/HTMLInputElement.h" |
|
203 #include "mozilla/dom/NodeFilterBinding.h" |
|
204 #include "mozilla/dom/OwningNonNull.h" |
|
205 #include "mozilla/dom/UndoManager.h" |
|
206 #include "mozilla/dom/WebComponentsBinding.h" |
|
207 #include "nsFrame.h" |
|
208 #include "nsDOMCaretPosition.h" |
|
209 #include "nsIDOMHTMLTextAreaElement.h" |
|
210 #include "nsViewportInfo.h" |
|
211 #include "nsIContentPermissionPrompt.h" |
|
212 #include "mozilla/StaticPtr.h" |
|
213 #include "nsITextControlElement.h" |
|
214 #include "nsIDOMNSEditableElement.h" |
|
215 #include "nsIEditor.h" |
|
216 #include "nsIDOMCSSStyleRule.h" |
|
217 #include "mozilla/css/Rule.h" |
|
218 #include "nsIDOMLocation.h" |
|
219 #include "nsIHttpChannelInternal.h" |
|
220 #include "nsISecurityConsoleMessage.h" |
|
221 #include "nsCharSeparatedTokenizer.h" |
|
222 #include "mozilla/dom/XPathEvaluator.h" |
|
223 #include "nsIDocumentEncoder.h" |
|
224 #include "nsIStructuredCloneContainer.h" |
|
225 #include "nsIMutableArray.h" |
|
226 #include "nsContentPermissionHelper.h" |
|
227 #include "mozilla/dom/DOMStringList.h" |
|
228 #include "nsWindowMemoryReporter.h" |
|
229 |
|
230 using namespace mozilla; |
|
231 using namespace mozilla::dom; |
|
232 |
|
233 typedef nsTArray<Link*> LinkArray; |
|
234 |
|
235 #ifdef PR_LOGGING |
|
236 static PRLogModuleInfo* gDocumentLeakPRLog; |
|
237 static PRLogModuleInfo* gCspPRLog; |
|
238 #endif |
|
239 |
|
240 #define NAME_NOT_VALID ((nsSimpleContentList*)1) |
|
241 |
|
242 nsIdentifierMapEntry::~nsIdentifierMapEntry() |
|
243 { |
|
244 } |
|
245 |
|
246 void |
|
247 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback) |
|
248 { |
|
249 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
|
250 "mIdentifierMap mNameContentList"); |
|
251 aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList)); |
|
252 |
|
253 if (mImageElement) { |
|
254 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
|
255 "mIdentifierMap mImageElement element"); |
|
256 nsIContent* imageElement = mImageElement; |
|
257 aCallback->NoteXPCOMChild(imageElement); |
|
258 } |
|
259 } |
|
260 |
|
261 bool |
|
262 nsIdentifierMapEntry::IsEmpty() |
|
263 { |
|
264 return mIdContentList.Count() == 0 && !mNameContentList && |
|
265 !mChangeCallbacks && !mImageElement; |
|
266 } |
|
267 |
|
268 Element* |
|
269 nsIdentifierMapEntry::GetIdElement() |
|
270 { |
|
271 return static_cast<Element*>(mIdContentList.SafeElementAt(0)); |
|
272 } |
|
273 |
|
274 Element* |
|
275 nsIdentifierMapEntry::GetImageIdElement() |
|
276 { |
|
277 return mImageElement ? mImageElement.get() : GetIdElement(); |
|
278 } |
|
279 |
|
280 void |
|
281 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements) |
|
282 { |
|
283 for (int32_t i = 0; i < mIdContentList.Count(); ++i) { |
|
284 aElements->AppendObject(static_cast<Element*>(mIdContentList[i])); |
|
285 } |
|
286 } |
|
287 |
|
288 void |
|
289 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, |
|
290 void* aData, bool aForImage) |
|
291 { |
|
292 if (!mChangeCallbacks) { |
|
293 mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>; |
|
294 if (!mChangeCallbacks) |
|
295 return; |
|
296 } |
|
297 |
|
298 ChangeCallback cc = { aCallback, aData, aForImage }; |
|
299 mChangeCallbacks->PutEntry(cc); |
|
300 } |
|
301 |
|
302 void |
|
303 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, |
|
304 void* aData, bool aForImage) |
|
305 { |
|
306 if (!mChangeCallbacks) |
|
307 return; |
|
308 ChangeCallback cc = { aCallback, aData, aForImage }; |
|
309 mChangeCallbacks->RemoveEntry(cc); |
|
310 if (mChangeCallbacks->Count() == 0) { |
|
311 mChangeCallbacks = nullptr; |
|
312 } |
|
313 } |
|
314 |
|
315 struct FireChangeArgs { |
|
316 Element* mFrom; |
|
317 Element* mTo; |
|
318 bool mImageOnly; |
|
319 bool mHaveImageOverride; |
|
320 }; |
|
321 |
|
322 namespace mozilla { |
|
323 namespace dom { |
|
324 |
|
325 static PLDHashOperator |
|
326 CustomDefinitionsTraverse(CustomElementHashKey* aKey, |
|
327 CustomElementDefinition* aDefinition, |
|
328 void* aArg) |
|
329 { |
|
330 nsCycleCollectionTraversalCallback* cb = |
|
331 static_cast<nsCycleCollectionTraversalCallback*>(aArg); |
|
332 |
|
333 nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks; |
|
334 |
|
335 if (callbacks->mAttributeChangedCallback.WasPassed()) { |
|
336 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
337 "mCustomDefinitions->mCallbacks->mAttributeChangedCallback"); |
|
338 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value()); |
|
339 } |
|
340 |
|
341 if (callbacks->mCreatedCallback.WasPassed()) { |
|
342 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
343 "mCustomDefinitions->mCallbacks->mCreatedCallback"); |
|
344 cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value()); |
|
345 } |
|
346 |
|
347 if (callbacks->mAttachedCallback.WasPassed()) { |
|
348 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
349 "mCustomDefinitions->mCallbacks->mAttachedCallback"); |
|
350 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value()); |
|
351 } |
|
352 |
|
353 if (callbacks->mDetachedCallback.WasPassed()) { |
|
354 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
355 "mCustomDefinitions->mCallbacks->mDetachedCallback"); |
|
356 cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value()); |
|
357 } |
|
358 |
|
359 return PL_DHASH_NEXT; |
|
360 } |
|
361 |
|
362 static PLDHashOperator |
|
363 CandidatesTraverse(CustomElementHashKey* aKey, |
|
364 nsTArray<nsRefPtr<Element>>* aData, |
|
365 void* aArg) |
|
366 { |
|
367 nsCycleCollectionTraversalCallback *cb = |
|
368 static_cast<nsCycleCollectionTraversalCallback*>(aArg); |
|
369 for (size_t i = 0; i < aData->Length(); ++i) { |
|
370 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element"); |
|
371 cb->NoteXPCOMChild(aData->ElementAt(i)); |
|
372 } |
|
373 return PL_DHASH_NEXT; |
|
374 } |
|
375 |
|
376 struct CustomDefinitionTraceArgs |
|
377 { |
|
378 const TraceCallbacks& callbacks; |
|
379 void* closure; |
|
380 }; |
|
381 |
|
382 static PLDHashOperator |
|
383 CustomDefinitionTrace(CustomElementHashKey *aKey, |
|
384 CustomElementDefinition *aData, |
|
385 void *aArg) |
|
386 { |
|
387 CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg); |
|
388 MOZ_ASSERT(aData, "Definition must not be null"); |
|
389 traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype", |
|
390 traceArgs->closure); |
|
391 return PL_DHASH_NEXT; |
|
392 } |
|
393 |
|
394 NS_IMPL_CYCLE_COLLECTION_CLASS(Registry) |
|
395 |
|
396 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry) |
|
397 CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure }; |
|
398 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace, |
|
399 &customDefinitionArgs); |
|
400 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
401 |
|
402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry) |
|
403 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb); |
|
404 tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb); |
|
405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
407 |
|
408 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry) |
|
409 tmp->mCustomDefinitions.Clear(); |
|
410 tmp->mCandidatesMap.Clear(); |
|
411 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
412 |
|
413 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry) |
|
414 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
415 NS_INTERFACE_MAP_END |
|
416 |
|
417 NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry) |
|
418 NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry) |
|
419 |
|
420 Registry::Registry() |
|
421 { |
|
422 mozilla::HoldJSObjects(this); |
|
423 } |
|
424 |
|
425 Registry::~Registry() |
|
426 { |
|
427 mozilla::DropJSObjects(this); |
|
428 } |
|
429 |
|
430 void |
|
431 CustomElementCallback::Call() |
|
432 { |
|
433 ErrorResult rv; |
|
434 switch (mType) { |
|
435 case nsIDocument::eCreated: |
|
436 // For the duration of this callback invocation, the element is being created |
|
437 // flag must be set to true. |
|
438 mOwnerData->mElementIsBeingCreated = true; |
|
439 mOwnerData->mCreatedCallbackInvoked = true; |
|
440 static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv); |
|
441 mOwnerData->mElementIsBeingCreated = false; |
|
442 break; |
|
443 case nsIDocument::eAttached: |
|
444 static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv); |
|
445 break; |
|
446 case nsIDocument::eDetached: |
|
447 static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv); |
|
448 break; |
|
449 case nsIDocument::eAttributeChanged: |
|
450 static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject, |
|
451 mArgs.name, mArgs.oldValue, mArgs.newValue, rv); |
|
452 break; |
|
453 } |
|
454 } |
|
455 |
|
456 void |
|
457 CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const |
|
458 { |
|
459 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject"); |
|
460 aCb.NoteXPCOMChild(mThisObject); |
|
461 |
|
462 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback"); |
|
463 aCb.NoteXPCOMChild(mCallback); |
|
464 } |
|
465 |
|
466 CustomElementCallback::CustomElementCallback(Element* aThisObject, |
|
467 nsIDocument::ElementCallbackType aCallbackType, |
|
468 mozilla::dom::CallbackFunction* aCallback, |
|
469 CustomElementData* aOwnerData) |
|
470 : mThisObject(aThisObject), |
|
471 mCallback(aCallback), |
|
472 mType(aCallbackType), |
|
473 mOwnerData(aOwnerData) |
|
474 { |
|
475 } |
|
476 |
|
477 CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype, |
|
478 nsIAtom* aType, |
|
479 nsIAtom* aLocalName, |
|
480 LifecycleCallbacks* aCallbacks, |
|
481 uint32_t aNamespaceID, |
|
482 uint32_t aDocOrder) |
|
483 : mPrototype(aPrototype), |
|
484 mType(aType), |
|
485 mLocalName(aLocalName), |
|
486 mCallbacks(aCallbacks), |
|
487 mNamespaceID(aNamespaceID), |
|
488 mDocOrder(aDocOrder) |
|
489 { |
|
490 } |
|
491 |
|
492 CustomElementData::CustomElementData(nsIAtom* aType) |
|
493 : mType(aType), |
|
494 mCurrentCallback(-1), |
|
495 mElementIsBeingCreated(false), |
|
496 mCreatedCallbackInvoked(true), |
|
497 mAssociatedMicroTask(-1) |
|
498 { |
|
499 } |
|
500 |
|
501 void |
|
502 CustomElementData::RunCallbackQueue() |
|
503 { |
|
504 // Note: It's possible to re-enter this method. |
|
505 while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) { |
|
506 mCallbackQueue[mCurrentCallback]->Call(); |
|
507 } |
|
508 |
|
509 mCallbackQueue.Clear(); |
|
510 mCurrentCallback = -1; |
|
511 } |
|
512 |
|
513 } // namespace dom |
|
514 } // namespace mozilla |
|
515 |
|
516 static PLDHashOperator |
|
517 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg) |
|
518 { |
|
519 FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg); |
|
520 // Don't fire image changes for non-image observers, and don't fire element |
|
521 // changes for image observers when an image override is active. |
|
522 if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) : |
|
523 args->mImageOnly) |
|
524 return PL_DHASH_NEXT; |
|
525 return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData) |
|
526 ? PL_DHASH_NEXT : PL_DHASH_REMOVE; |
|
527 } |
|
528 |
|
529 void |
|
530 nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement, |
|
531 Element* aNewElement, |
|
532 bool aImageOnly) |
|
533 { |
|
534 if (!mChangeCallbacks) |
|
535 return; |
|
536 |
|
537 FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement }; |
|
538 mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args); |
|
539 } |
|
540 |
|
541 bool |
|
542 nsIdentifierMapEntry::AddIdElement(Element* aElement) |
|
543 { |
|
544 NS_PRECONDITION(aElement, "Must have element"); |
|
545 NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0, |
|
546 "Why is null in our list?"); |
|
547 |
|
548 #ifdef DEBUG |
|
549 Element* currentElement = |
|
550 static_cast<Element*>(mIdContentList.SafeElementAt(0)); |
|
551 #endif |
|
552 |
|
553 // Common case |
|
554 if (mIdContentList.Count() == 0) { |
|
555 if (!mIdContentList.AppendElement(aElement)) |
|
556 return false; |
|
557 NS_ASSERTION(currentElement == nullptr, "How did that happen?"); |
|
558 FireChangeCallbacks(nullptr, aElement); |
|
559 return true; |
|
560 } |
|
561 |
|
562 // We seem to have multiple content nodes for the same id, or XUL is messing |
|
563 // with us. Search for the right place to insert the content. |
|
564 int32_t start = 0; |
|
565 int32_t end = mIdContentList.Count(); |
|
566 do { |
|
567 NS_ASSERTION(start < end, "Bogus start/end"); |
|
568 |
|
569 int32_t cur = (start + end) / 2; |
|
570 NS_ASSERTION(cur >= start && cur < end, "What happened here?"); |
|
571 |
|
572 Element* curElement = static_cast<Element*>(mIdContentList[cur]); |
|
573 if (curElement == aElement) { |
|
574 // Already in the list, so already in the right spot. Get out of here. |
|
575 // XXXbz this only happens because XUL does all sorts of random |
|
576 // UpdateIdTableEntry calls. Hate, hate, hate! |
|
577 return true; |
|
578 } |
|
579 |
|
580 if (nsContentUtils::PositionIsBefore(aElement, curElement)) { |
|
581 end = cur; |
|
582 } else { |
|
583 start = cur + 1; |
|
584 } |
|
585 } while (start != end); |
|
586 |
|
587 if (!mIdContentList.InsertElementAt(aElement, start)) |
|
588 return false; |
|
589 |
|
590 if (start == 0) { |
|
591 Element* oldElement = |
|
592 static_cast<Element*>(mIdContentList.SafeElementAt(1)); |
|
593 NS_ASSERTION(currentElement == oldElement, "How did that happen?"); |
|
594 FireChangeCallbacks(oldElement, aElement); |
|
595 } |
|
596 return true; |
|
597 } |
|
598 |
|
599 void |
|
600 nsIdentifierMapEntry::RemoveIdElement(Element* aElement) |
|
601 { |
|
602 NS_PRECONDITION(aElement, "Missing element"); |
|
603 |
|
604 // This should only be called while the document is in an update. |
|
605 // Assertions near the call to this method guarantee this. |
|
606 |
|
607 // This could fire in OOM situations |
|
608 // Only assert this in HTML documents for now as XUL does all sorts of weird |
|
609 // crap. |
|
610 NS_ASSERTION(!aElement->OwnerDoc()->IsHTML() || |
|
611 mIdContentList.IndexOf(aElement) >= 0, |
|
612 "Removing id entry that doesn't exist"); |
|
613 |
|
614 // XXXbz should this ever Compact() I guess when all the content is gone |
|
615 // we'll just get cleaned up in the natural order of things... |
|
616 Element* currentElement = |
|
617 static_cast<Element*>(mIdContentList.SafeElementAt(0)); |
|
618 mIdContentList.RemoveElement(aElement); |
|
619 if (currentElement == aElement) { |
|
620 FireChangeCallbacks(currentElement, |
|
621 static_cast<Element*>(mIdContentList.SafeElementAt(0))); |
|
622 } |
|
623 } |
|
624 |
|
625 void |
|
626 nsIdentifierMapEntry::SetImageElement(Element* aElement) |
|
627 { |
|
628 Element* oldElement = GetImageIdElement(); |
|
629 mImageElement = aElement; |
|
630 Element* newElement = GetImageIdElement(); |
|
631 if (oldElement != newElement) { |
|
632 FireChangeCallbacks(oldElement, newElement, true); |
|
633 } |
|
634 } |
|
635 |
|
636 void |
|
637 nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) |
|
638 { |
|
639 if (!mNameContentList) { |
|
640 mNameContentList = new nsSimpleContentList(aNode); |
|
641 } |
|
642 |
|
643 mNameContentList->AppendElement(aElement); |
|
644 } |
|
645 |
|
646 void |
|
647 nsIdentifierMapEntry::RemoveNameElement(Element* aElement) |
|
648 { |
|
649 if (mNameContentList) { |
|
650 mNameContentList->RemoveElement(aElement); |
|
651 } |
|
652 } |
|
653 |
|
654 bool |
|
655 nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() |
|
656 { |
|
657 Element* idElement = GetIdElement(); |
|
658 return idElement && |
|
659 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement); |
|
660 } |
|
661 |
|
662 // static |
|
663 size_t |
|
664 nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry, |
|
665 MallocSizeOf aMallocSizeOf, |
|
666 void*) |
|
667 { |
|
668 return aEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
|
669 } |
|
670 |
|
671 // Helper structs for the content->subdoc map |
|
672 |
|
673 class SubDocMapEntry : public PLDHashEntryHdr |
|
674 { |
|
675 public: |
|
676 // Both of these are strong references |
|
677 Element *mKey; // must be first, to look like PLDHashEntryStub |
|
678 nsIDocument *mSubDocument; |
|
679 }; |
|
680 |
|
681 struct FindContentData |
|
682 { |
|
683 FindContentData(nsIDocument *aSubDoc) |
|
684 : mSubDocument(aSubDoc), mResult(nullptr) |
|
685 { |
|
686 } |
|
687 |
|
688 nsISupports *mSubDocument; |
|
689 Element *mResult; |
|
690 }; |
|
691 |
|
692 |
|
693 /** |
|
694 * A struct that holds all the information about a radio group. |
|
695 */ |
|
696 struct nsRadioGroupStruct |
|
697 { |
|
698 nsRadioGroupStruct() |
|
699 : mRequiredRadioCount(0) |
|
700 , mGroupSuffersFromValueMissing(false) |
|
701 {} |
|
702 |
|
703 /** |
|
704 * A strong pointer to the currently selected radio button. |
|
705 */ |
|
706 nsRefPtr<HTMLInputElement> mSelectedRadioButton; |
|
707 nsCOMArray<nsIFormControl> mRadioButtons; |
|
708 uint32_t mRequiredRadioCount; |
|
709 bool mGroupSuffersFromValueMissing; |
|
710 }; |
|
711 |
|
712 |
|
713 nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument) |
|
714 { |
|
715 mLength = -1; |
|
716 // Not reference counted to avoid circular references. |
|
717 // The document will tell us when its going away. |
|
718 mDocument = aDocument; |
|
719 mDocument->AddObserver(this); |
|
720 } |
|
721 |
|
722 nsDOMStyleSheetList::~nsDOMStyleSheetList() |
|
723 { |
|
724 if (mDocument) { |
|
725 mDocument->RemoveObserver(this); |
|
726 } |
|
727 } |
|
728 |
|
729 NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList, |
|
730 nsIDocumentObserver, |
|
731 nsIMutationObserver) |
|
732 |
|
733 uint32_t |
|
734 nsDOMStyleSheetList::Length() |
|
735 { |
|
736 if (!mDocument) { |
|
737 return 0; |
|
738 } |
|
739 |
|
740 // XXX Find the number and then cache it. We'll use the |
|
741 // observer notification to figure out if new ones have |
|
742 // been added or removed. |
|
743 if (-1 == mLength) { |
|
744 mLength = mDocument->GetNumberOfStyleSheets(); |
|
745 |
|
746 #ifdef DEBUG |
|
747 int32_t i; |
|
748 for (i = 0; i < mLength; i++) { |
|
749 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i); |
|
750 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet)); |
|
751 NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet"); |
|
752 } |
|
753 #endif |
|
754 } |
|
755 return mLength; |
|
756 } |
|
757 |
|
758 nsCSSStyleSheet* |
|
759 nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound) |
|
760 { |
|
761 if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) { |
|
762 aFound = false; |
|
763 return nullptr; |
|
764 } |
|
765 |
|
766 aFound = true; |
|
767 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex); |
|
768 NS_ASSERTION(sheet, "Must have a sheet"); |
|
769 |
|
770 return static_cast<nsCSSStyleSheet*>(sheet); |
|
771 } |
|
772 |
|
773 void |
|
774 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode) |
|
775 { |
|
776 mDocument = nullptr; |
|
777 } |
|
778 |
|
779 void |
|
780 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument, |
|
781 nsIStyleSheet* aStyleSheet, |
|
782 bool aDocumentSheet) |
|
783 { |
|
784 if (aDocumentSheet && -1 != mLength) { |
|
785 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet)); |
|
786 if (domss) { |
|
787 mLength++; |
|
788 } |
|
789 } |
|
790 } |
|
791 |
|
792 void |
|
793 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument, |
|
794 nsIStyleSheet* aStyleSheet, |
|
795 bool aDocumentSheet) |
|
796 { |
|
797 if (aDocumentSheet && -1 != mLength) { |
|
798 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet)); |
|
799 if (domss) { |
|
800 mLength--; |
|
801 } |
|
802 } |
|
803 } |
|
804 |
|
805 // nsOnloadBlocker implementation |
|
806 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest) |
|
807 |
|
808 NS_IMETHODIMP |
|
809 nsOnloadBlocker::GetName(nsACString &aResult) |
|
810 { |
|
811 aResult.AssignLiteral("about:document-onload-blocker"); |
|
812 return NS_OK; |
|
813 } |
|
814 |
|
815 NS_IMETHODIMP |
|
816 nsOnloadBlocker::IsPending(bool *_retval) |
|
817 { |
|
818 *_retval = true; |
|
819 return NS_OK; |
|
820 } |
|
821 |
|
822 NS_IMETHODIMP |
|
823 nsOnloadBlocker::GetStatus(nsresult *status) |
|
824 { |
|
825 *status = NS_OK; |
|
826 return NS_OK; |
|
827 } |
|
828 |
|
829 NS_IMETHODIMP |
|
830 nsOnloadBlocker::Cancel(nsresult status) |
|
831 { |
|
832 return NS_OK; |
|
833 } |
|
834 NS_IMETHODIMP |
|
835 nsOnloadBlocker::Suspend(void) |
|
836 { |
|
837 return NS_OK; |
|
838 } |
|
839 NS_IMETHODIMP |
|
840 nsOnloadBlocker::Resume(void) |
|
841 { |
|
842 return NS_OK; |
|
843 } |
|
844 |
|
845 NS_IMETHODIMP |
|
846 nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup) |
|
847 { |
|
848 *aLoadGroup = nullptr; |
|
849 return NS_OK; |
|
850 } |
|
851 |
|
852 NS_IMETHODIMP |
|
853 nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup) |
|
854 { |
|
855 return NS_OK; |
|
856 } |
|
857 |
|
858 NS_IMETHODIMP |
|
859 nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags) |
|
860 { |
|
861 *aLoadFlags = nsIRequest::LOAD_NORMAL; |
|
862 return NS_OK; |
|
863 } |
|
864 |
|
865 NS_IMETHODIMP |
|
866 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) |
|
867 { |
|
868 return NS_OK; |
|
869 } |
|
870 |
|
871 // ================================================================== |
|
872 |
|
873 nsExternalResourceMap::nsExternalResourceMap() |
|
874 : mHaveShutDown(false) |
|
875 { |
|
876 } |
|
877 |
|
878 nsIDocument* |
|
879 nsExternalResourceMap::RequestResource(nsIURI* aURI, |
|
880 nsINode* aRequestingNode, |
|
881 nsDocument* aDisplayDocument, |
|
882 ExternalResourceLoad** aPendingLoad) |
|
883 { |
|
884 // If we ever start allowing non-same-origin loads here, we might need to do |
|
885 // something interesting with aRequestingPrincipal even for the hashtable |
|
886 // gets. |
|
887 NS_PRECONDITION(aURI, "Must have a URI"); |
|
888 NS_PRECONDITION(aRequestingNode, "Must have a node"); |
|
889 *aPendingLoad = nullptr; |
|
890 if (mHaveShutDown) { |
|
891 return nullptr; |
|
892 } |
|
893 |
|
894 // First, make sure we strip the ref from aURI. |
|
895 nsCOMPtr<nsIURI> clone; |
|
896 nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone)); |
|
897 if (NS_FAILED(rv) || !clone) { |
|
898 return nullptr; |
|
899 } |
|
900 |
|
901 ExternalResource* resource; |
|
902 mMap.Get(clone, &resource); |
|
903 if (resource) { |
|
904 return resource->mDocument; |
|
905 } |
|
906 |
|
907 nsRefPtr<PendingLoad> load; |
|
908 mPendingLoads.Get(clone, getter_AddRefs(load)); |
|
909 if (load) { |
|
910 load.forget(aPendingLoad); |
|
911 return nullptr; |
|
912 } |
|
913 |
|
914 load = new PendingLoad(aDisplayDocument); |
|
915 |
|
916 mPendingLoads.Put(clone, load); |
|
917 |
|
918 if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) { |
|
919 // Make sure we don't thrash things by trying this load again, since |
|
920 // chances are it failed for good reasons (security check, etc). |
|
921 AddExternalResource(clone, nullptr, nullptr, aDisplayDocument); |
|
922 } else { |
|
923 load.forget(aPendingLoad); |
|
924 } |
|
925 |
|
926 return nullptr; |
|
927 } |
|
928 |
|
929 struct |
|
930 nsExternalResourceEnumArgs |
|
931 { |
|
932 nsIDocument::nsSubDocEnumFunc callback; |
|
933 void *data; |
|
934 }; |
|
935 |
|
936 static PLDHashOperator |
|
937 ExternalResourceEnumerator(nsIURI* aKey, |
|
938 nsExternalResourceMap::ExternalResource* aData, |
|
939 void* aClosure) |
|
940 { |
|
941 nsExternalResourceEnumArgs* args = |
|
942 static_cast<nsExternalResourceEnumArgs*>(aClosure); |
|
943 bool next = |
|
944 aData->mDocument ? args->callback(aData->mDocument, args->data) : true; |
|
945 return next ? PL_DHASH_NEXT : PL_DHASH_STOP; |
|
946 } |
|
947 |
|
948 void |
|
949 nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, |
|
950 void* aData) |
|
951 { |
|
952 nsExternalResourceEnumArgs args = { aCallback, aData }; |
|
953 mMap.EnumerateRead(ExternalResourceEnumerator, &args); |
|
954 } |
|
955 |
|
956 static PLDHashOperator |
|
957 ExternalResourceTraverser(nsIURI* aKey, |
|
958 nsExternalResourceMap::ExternalResource* aData, |
|
959 void* aClosure) |
|
960 { |
|
961 nsCycleCollectionTraversalCallback *cb = |
|
962 static_cast<nsCycleCollectionTraversalCallback*>(aClosure); |
|
963 |
|
964 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
965 "mExternalResourceMap.mMap entry" |
|
966 "->mDocument"); |
|
967 cb->NoteXPCOMChild(aData->mDocument); |
|
968 |
|
969 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
970 "mExternalResourceMap.mMap entry" |
|
971 "->mViewer"); |
|
972 cb->NoteXPCOMChild(aData->mViewer); |
|
973 |
|
974 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
975 "mExternalResourceMap.mMap entry" |
|
976 "->mLoadGroup"); |
|
977 cb->NoteXPCOMChild(aData->mLoadGroup); |
|
978 |
|
979 return PL_DHASH_NEXT; |
|
980 } |
|
981 |
|
982 void |
|
983 nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const |
|
984 { |
|
985 // mPendingLoads will get cleared out as the requests complete, so |
|
986 // no need to worry about those here. |
|
987 mMap.EnumerateRead(ExternalResourceTraverser, aCallback); |
|
988 } |
|
989 |
|
990 static PLDHashOperator |
|
991 ExternalResourceHider(nsIURI* aKey, |
|
992 nsExternalResourceMap::ExternalResource* aData, |
|
993 void* aClosure) |
|
994 { |
|
995 if (aData->mViewer) { |
|
996 aData->mViewer->Hide(); |
|
997 } |
|
998 return PL_DHASH_NEXT; |
|
999 } |
|
1000 |
|
1001 void |
|
1002 nsExternalResourceMap::HideViewers() |
|
1003 { |
|
1004 mMap.EnumerateRead(ExternalResourceHider, nullptr); |
|
1005 } |
|
1006 |
|
1007 static PLDHashOperator |
|
1008 ExternalResourceShower(nsIURI* aKey, |
|
1009 nsExternalResourceMap::ExternalResource* aData, |
|
1010 void* aClosure) |
|
1011 { |
|
1012 if (aData->mViewer) { |
|
1013 aData->mViewer->Show(); |
|
1014 } |
|
1015 return PL_DHASH_NEXT; |
|
1016 } |
|
1017 |
|
1018 void |
|
1019 nsExternalResourceMap::ShowViewers() |
|
1020 { |
|
1021 mMap.EnumerateRead(ExternalResourceShower, nullptr); |
|
1022 } |
|
1023 |
|
1024 void |
|
1025 TransferZoomLevels(nsIDocument* aFromDoc, |
|
1026 nsIDocument* aToDoc) |
|
1027 { |
|
1028 NS_ABORT_IF_FALSE(aFromDoc && aToDoc, |
|
1029 "transferring zoom levels from/to null doc"); |
|
1030 |
|
1031 nsIPresShell* fromShell = aFromDoc->GetShell(); |
|
1032 if (!fromShell) |
|
1033 return; |
|
1034 |
|
1035 nsPresContext* fromCtxt = fromShell->GetPresContext(); |
|
1036 if (!fromCtxt) |
|
1037 return; |
|
1038 |
|
1039 nsIPresShell* toShell = aToDoc->GetShell(); |
|
1040 if (!toShell) |
|
1041 return; |
|
1042 |
|
1043 nsPresContext* toCtxt = toShell->GetPresContext(); |
|
1044 if (!toCtxt) |
|
1045 return; |
|
1046 |
|
1047 toCtxt->SetFullZoom(fromCtxt->GetFullZoom()); |
|
1048 toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize()); |
|
1049 toCtxt->SetTextZoom(fromCtxt->TextZoom()); |
|
1050 } |
|
1051 |
|
1052 void |
|
1053 TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc) |
|
1054 { |
|
1055 NS_ABORT_IF_FALSE(aFromDoc && aToDoc, |
|
1056 "transferring showing state from/to null doc"); |
|
1057 |
|
1058 if (aFromDoc->IsShowing()) { |
|
1059 aToDoc->OnPageShow(true, nullptr); |
|
1060 } |
|
1061 } |
|
1062 |
|
1063 nsresult |
|
1064 nsExternalResourceMap::AddExternalResource(nsIURI* aURI, |
|
1065 nsIContentViewer* aViewer, |
|
1066 nsILoadGroup* aLoadGroup, |
|
1067 nsIDocument* aDisplayDocument) |
|
1068 { |
|
1069 NS_PRECONDITION(aURI, "Unexpected call"); |
|
1070 NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup), |
|
1071 "Must have both or neither"); |
|
1072 |
|
1073 nsRefPtr<PendingLoad> load; |
|
1074 mPendingLoads.Get(aURI, getter_AddRefs(load)); |
|
1075 mPendingLoads.Remove(aURI); |
|
1076 |
|
1077 nsresult rv = NS_OK; |
|
1078 |
|
1079 nsCOMPtr<nsIDocument> doc; |
|
1080 if (aViewer) { |
|
1081 doc = aViewer->GetDocument(); |
|
1082 NS_ASSERTION(doc, "Must have a document"); |
|
1083 |
|
1084 nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc); |
|
1085 if (xulDoc) { |
|
1086 // We don't handle XUL stuff here yet. |
|
1087 rv = NS_ERROR_NOT_AVAILABLE; |
|
1088 } else { |
|
1089 doc->SetDisplayDocument(aDisplayDocument); |
|
1090 |
|
1091 // Make sure that hiding our viewer will tear down its presentation. |
|
1092 aViewer->SetSticky(false); |
|
1093 |
|
1094 rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0)); |
|
1095 if (NS_SUCCEEDED(rv)) { |
|
1096 rv = aViewer->Open(nullptr, nullptr); |
|
1097 } |
|
1098 } |
|
1099 |
|
1100 if (NS_FAILED(rv)) { |
|
1101 doc = nullptr; |
|
1102 aViewer = nullptr; |
|
1103 aLoadGroup = nullptr; |
|
1104 } |
|
1105 } |
|
1106 |
|
1107 ExternalResource* newResource = new ExternalResource(); |
|
1108 mMap.Put(aURI, newResource); |
|
1109 |
|
1110 newResource->mDocument = doc; |
|
1111 newResource->mViewer = aViewer; |
|
1112 newResource->mLoadGroup = aLoadGroup; |
|
1113 if (doc) { |
|
1114 TransferZoomLevels(aDisplayDocument, doc); |
|
1115 TransferShowingState(aDisplayDocument, doc); |
|
1116 } |
|
1117 |
|
1118 const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers(); |
|
1119 for (uint32_t i = 0; i < obs.Length(); ++i) { |
|
1120 obs[i]->Observe(doc, "external-resource-document-created", nullptr); |
|
1121 } |
|
1122 |
|
1123 return rv; |
|
1124 } |
|
1125 |
|
1126 NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad, |
|
1127 nsIStreamListener, |
|
1128 nsIRequestObserver) |
|
1129 |
|
1130 NS_IMETHODIMP |
|
1131 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest, |
|
1132 nsISupports *aContext) |
|
1133 { |
|
1134 nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap(); |
|
1135 if (map.HaveShutDown()) { |
|
1136 return NS_BINDING_ABORTED; |
|
1137 } |
|
1138 |
|
1139 nsCOMPtr<nsIContentViewer> viewer; |
|
1140 nsCOMPtr<nsILoadGroup> loadGroup; |
|
1141 nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer), |
|
1142 getter_AddRefs(loadGroup)); |
|
1143 |
|
1144 // Make sure to do this no matter what |
|
1145 nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup, |
|
1146 mDisplayDocument); |
|
1147 if (NS_FAILED(rv)) { |
|
1148 return rv; |
|
1149 } |
|
1150 if (NS_FAILED(rv2)) { |
|
1151 mTargetListener = nullptr; |
|
1152 return rv2; |
|
1153 } |
|
1154 |
|
1155 return mTargetListener->OnStartRequest(aRequest, aContext); |
|
1156 } |
|
1157 |
|
1158 nsresult |
|
1159 nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest, |
|
1160 nsIContentViewer** aViewer, |
|
1161 nsILoadGroup** aLoadGroup) |
|
1162 { |
|
1163 NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest"); |
|
1164 *aViewer = nullptr; |
|
1165 *aLoadGroup = nullptr; |
|
1166 |
|
1167 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); |
|
1168 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); |
|
1169 |
|
1170 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest)); |
|
1171 if (httpChannel) { |
|
1172 bool requestSucceeded; |
|
1173 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) || |
|
1174 !requestSucceeded) { |
|
1175 // Bail out on this load, since it looks like we have an HTTP error page |
|
1176 return NS_BINDING_ABORTED; |
|
1177 } |
|
1178 } |
|
1179 |
|
1180 nsAutoCString type; |
|
1181 chan->GetContentType(type); |
|
1182 |
|
1183 nsCOMPtr<nsILoadGroup> loadGroup; |
|
1184 chan->GetLoadGroup(getter_AddRefs(loadGroup)); |
|
1185 |
|
1186 // Give this document its own loadgroup |
|
1187 nsCOMPtr<nsILoadGroup> newLoadGroup = |
|
1188 do_CreateInstance(NS_LOADGROUP_CONTRACTID); |
|
1189 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY); |
|
1190 newLoadGroup->SetLoadGroup(loadGroup); |
|
1191 |
|
1192 nsCOMPtr<nsIInterfaceRequestor> callbacks; |
|
1193 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); |
|
1194 |
|
1195 nsCOMPtr<nsIInterfaceRequestor> newCallbacks = |
|
1196 new LoadgroupCallbacks(callbacks); |
|
1197 newLoadGroup->SetNotificationCallbacks(newCallbacks); |
|
1198 |
|
1199 // This is some serious hackery cribbed from docshell |
|
1200 nsCOMPtr<nsICategoryManager> catMan = |
|
1201 do_GetService(NS_CATEGORYMANAGER_CONTRACTID); |
|
1202 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE); |
|
1203 nsXPIDLCString contractId; |
|
1204 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(), |
|
1205 getter_Copies(contractId)); |
|
1206 NS_ENSURE_SUCCESS(rv, rv); |
|
1207 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = |
|
1208 do_GetService(contractId); |
|
1209 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); |
|
1210 |
|
1211 nsCOMPtr<nsIContentViewer> viewer; |
|
1212 nsCOMPtr<nsIStreamListener> listener; |
|
1213 rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup, |
|
1214 type.get(), nullptr, nullptr, |
|
1215 getter_AddRefs(listener), |
|
1216 getter_AddRefs(viewer)); |
|
1217 NS_ENSURE_SUCCESS(rv, rv); |
|
1218 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED); |
|
1219 |
|
1220 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener); |
|
1221 if (!parser) { |
|
1222 /// We don't want to deal with the various fake documents yet |
|
1223 return NS_ERROR_NOT_IMPLEMENTED; |
|
1224 } |
|
1225 |
|
1226 // We can't handle HTML and other weird things here yet. |
|
1227 nsIContentSink* sink = parser->GetContentSink(); |
|
1228 nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink); |
|
1229 if (!xmlSink) { |
|
1230 return NS_ERROR_NOT_IMPLEMENTED; |
|
1231 } |
|
1232 |
|
1233 listener.swap(mTargetListener); |
|
1234 viewer.forget(aViewer); |
|
1235 newLoadGroup.forget(aLoadGroup); |
|
1236 return NS_OK; |
|
1237 } |
|
1238 |
|
1239 NS_IMETHODIMP |
|
1240 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest, |
|
1241 nsISupports* aContext, |
|
1242 nsIInputStream* aStream, |
|
1243 uint64_t aOffset, |
|
1244 uint32_t aCount) |
|
1245 { |
|
1246 NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!"); |
|
1247 if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) { |
|
1248 return NS_BINDING_ABORTED; |
|
1249 } |
|
1250 return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, |
|
1251 aCount); |
|
1252 } |
|
1253 |
|
1254 NS_IMETHODIMP |
|
1255 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest, |
|
1256 nsISupports* aContext, |
|
1257 nsresult aStatus) |
|
1258 { |
|
1259 // mTargetListener might be null if SetupViewer or AddExternalResource failed |
|
1260 if (mTargetListener) { |
|
1261 nsCOMPtr<nsIStreamListener> listener; |
|
1262 mTargetListener.swap(listener); |
|
1263 return listener->OnStopRequest(aRequest, aContext, aStatus); |
|
1264 } |
|
1265 |
|
1266 return NS_OK; |
|
1267 } |
|
1268 |
|
1269 nsresult |
|
1270 nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI, |
|
1271 nsINode* aRequestingNode) |
|
1272 { |
|
1273 NS_PRECONDITION(aURI, "Must have a URI"); |
|
1274 NS_PRECONDITION(aRequestingNode, "Must have a node"); |
|
1275 |
|
1276 // Time to start a load. First, the security checks. |
|
1277 |
|
1278 nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal(); |
|
1279 |
|
1280 nsresult rv = nsContentUtils::GetSecurityManager()-> |
|
1281 CheckLoadURIWithPrincipal(requestingPrincipal, aURI, |
|
1282 nsIScriptSecurityManager::STANDARD); |
|
1283 NS_ENSURE_SUCCESS(rv, rv); |
|
1284 |
|
1285 // Allow data URIs and other URI's that inherit their principal by passing |
|
1286 // true as the 3rd argument of CheckMayLoad, since we want |
|
1287 // to allow external resources from data URIs regardless of the difference |
|
1288 // in URI scheme. |
|
1289 rv = requestingPrincipal->CheckMayLoad(aURI, true, true); |
|
1290 NS_ENSURE_SUCCESS(rv, rv); |
|
1291 |
|
1292 int16_t shouldLoad = nsIContentPolicy::ACCEPT; |
|
1293 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER, |
|
1294 aURI, |
|
1295 requestingPrincipal, |
|
1296 aRequestingNode, |
|
1297 EmptyCString(), //mime guess |
|
1298 nullptr, //extra |
|
1299 &shouldLoad, |
|
1300 nsContentUtils::GetContentPolicy(), |
|
1301 nsContentUtils::GetSecurityManager()); |
|
1302 if (NS_FAILED(rv)) return rv; |
|
1303 if (NS_CP_REJECTED(shouldLoad)) { |
|
1304 // Disallowed by content policy |
|
1305 return NS_ERROR_CONTENT_BLOCKED; |
|
1306 } |
|
1307 |
|
1308 nsIDocument* doc = aRequestingNode->OwnerDoc(); |
|
1309 |
|
1310 nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker(); |
|
1311 NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY); |
|
1312 |
|
1313 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup(); |
|
1314 nsCOMPtr<nsIChannel> channel; |
|
1315 rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, loadGroup, req); |
|
1316 NS_ENSURE_SUCCESS(rv, rv); |
|
1317 |
|
1318 mURI = aURI; |
|
1319 |
|
1320 return channel->AsyncOpen(this, nullptr); |
|
1321 } |
|
1322 |
|
1323 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks, |
|
1324 nsIInterfaceRequestor) |
|
1325 |
|
1326 #define IMPL_SHIM(_i) \ |
|
1327 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i) |
|
1328 |
|
1329 IMPL_SHIM(nsILoadContext) |
|
1330 IMPL_SHIM(nsIProgressEventSink) |
|
1331 IMPL_SHIM(nsIChannelEventSink) |
|
1332 IMPL_SHIM(nsISecurityEventSink) |
|
1333 IMPL_SHIM(nsIApplicationCacheContainer) |
|
1334 |
|
1335 #undef IMPL_SHIM |
|
1336 |
|
1337 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i)) |
|
1338 |
|
1339 #define TRY_SHIM(_i) \ |
|
1340 PR_BEGIN_MACRO \ |
|
1341 if (IID_IS(_i)) { \ |
|
1342 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \ |
|
1343 if (!real) { \ |
|
1344 return NS_NOINTERFACE; \ |
|
1345 } \ |
|
1346 nsCOMPtr<_i> shim = new _i##Shim(this, real); \ |
|
1347 if (!shim) { \ |
|
1348 return NS_ERROR_OUT_OF_MEMORY; \ |
|
1349 } \ |
|
1350 shim.forget(aSink); \ |
|
1351 return NS_OK; \ |
|
1352 } \ |
|
1353 PR_END_MACRO |
|
1354 |
|
1355 NS_IMETHODIMP |
|
1356 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID, |
|
1357 void **aSink) |
|
1358 { |
|
1359 if (mCallbacks && |
|
1360 (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) || |
|
1361 IID_IS(nsITabChild))) { |
|
1362 return mCallbacks->GetInterface(aIID, aSink); |
|
1363 } |
|
1364 |
|
1365 *aSink = nullptr; |
|
1366 |
|
1367 TRY_SHIM(nsILoadContext); |
|
1368 TRY_SHIM(nsIProgressEventSink); |
|
1369 TRY_SHIM(nsIChannelEventSink); |
|
1370 TRY_SHIM(nsISecurityEventSink); |
|
1371 TRY_SHIM(nsIApplicationCacheContainer); |
|
1372 |
|
1373 return NS_NOINTERFACE; |
|
1374 } |
|
1375 |
|
1376 #undef TRY_SHIM |
|
1377 #undef IID_IS |
|
1378 |
|
1379 nsExternalResourceMap::ExternalResource::~ExternalResource() |
|
1380 { |
|
1381 if (mViewer) { |
|
1382 mViewer->Close(nullptr); |
|
1383 mViewer->Destroy(); |
|
1384 } |
|
1385 } |
|
1386 |
|
1387 // ================================================================== |
|
1388 // = |
|
1389 // ================================================================== |
|
1390 |
|
1391 // If we ever have an nsIDocumentObserver notification for stylesheet title |
|
1392 // changes we should update the list from that instead of overriding |
|
1393 // EnsureFresh. |
|
1394 class nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList |
|
1395 { |
|
1396 public: |
|
1397 nsDOMStyleSheetSetList(nsIDocument* aDocument); |
|
1398 |
|
1399 void Disconnect() |
|
1400 { |
|
1401 mDocument = nullptr; |
|
1402 } |
|
1403 |
|
1404 virtual void EnsureFresh() MOZ_OVERRIDE; |
|
1405 |
|
1406 protected: |
|
1407 nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it |
|
1408 // dies. |
|
1409 }; |
|
1410 |
|
1411 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument) |
|
1412 : mDocument(aDocument) |
|
1413 { |
|
1414 NS_ASSERTION(mDocument, "Must have document!"); |
|
1415 } |
|
1416 |
|
1417 void |
|
1418 nsDOMStyleSheetSetList::EnsureFresh() |
|
1419 { |
|
1420 mNames.Clear(); |
|
1421 |
|
1422 if (!mDocument) { |
|
1423 return; // Spec says "no exceptions", and we have no style sets if we have |
|
1424 // no document, for sure |
|
1425 } |
|
1426 |
|
1427 int32_t count = mDocument->GetNumberOfStyleSheets(); |
|
1428 nsAutoString title; |
|
1429 for (int32_t index = 0; index < count; index++) { |
|
1430 nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index); |
|
1431 NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
|
1432 sheet->GetTitle(title); |
|
1433 if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) { |
|
1434 return; |
|
1435 } |
|
1436 } |
|
1437 } |
|
1438 |
|
1439 // ================================================================== |
|
1440 nsIDocument::SelectorCache::SelectorCache() |
|
1441 : nsExpirationTracker<SelectorCacheKey, 4>(1000) { } |
|
1442 |
|
1443 // CacheList takes ownership of aSelectorList. |
|
1444 void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector, |
|
1445 nsCSSSelectorList* aSelectorList) |
|
1446 { |
|
1447 SelectorCacheKey* key = new SelectorCacheKey(aSelector); |
|
1448 mTable.Put(key->mKey, aSelectorList); |
|
1449 AddObject(key); |
|
1450 } |
|
1451 |
|
1452 class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable |
|
1453 { |
|
1454 public: |
|
1455 explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete) |
|
1456 : mSelector(aToDelete) |
|
1457 { |
|
1458 MOZ_COUNT_CTOR(SelectorCacheKeyDeleter); |
|
1459 } |
|
1460 |
|
1461 ~SelectorCacheKeyDeleter() |
|
1462 { |
|
1463 MOZ_COUNT_DTOR(SelectorCacheKeyDeleter); |
|
1464 } |
|
1465 |
|
1466 NS_IMETHOD Run() |
|
1467 { |
|
1468 return NS_OK; |
|
1469 } |
|
1470 |
|
1471 private: |
|
1472 nsAutoPtr<SelectorCacheKey> mSelector; |
|
1473 }; |
|
1474 |
|
1475 void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector) |
|
1476 { |
|
1477 RemoveObject(aSelector); |
|
1478 mTable.Remove(aSelector->mKey); |
|
1479 nsCOMPtr<nsIRunnable> runnable = new SelectorCacheKeyDeleter(aSelector); |
|
1480 NS_DispatchToCurrentThread(runnable); |
|
1481 } |
|
1482 |
|
1483 |
|
1484 struct nsIDocument::FrameRequest |
|
1485 { |
|
1486 FrameRequest(const FrameRequestCallbackHolder& aCallback, |
|
1487 int32_t aHandle) : |
|
1488 mCallback(aCallback), |
|
1489 mHandle(aHandle) |
|
1490 {} |
|
1491 |
|
1492 // Conversion operator so that we can append these to a |
|
1493 // FrameRequestCallbackList |
|
1494 operator const FrameRequestCallbackHolder& () const { |
|
1495 return mCallback; |
|
1496 } |
|
1497 |
|
1498 // Comparator operators to allow RemoveElementSorted with an |
|
1499 // integer argument on arrays of FrameRequest |
|
1500 bool operator==(int32_t aHandle) const { |
|
1501 return mHandle == aHandle; |
|
1502 } |
|
1503 bool operator<(int32_t aHandle) const { |
|
1504 return mHandle < aHandle; |
|
1505 } |
|
1506 |
|
1507 FrameRequestCallbackHolder mCallback; |
|
1508 int32_t mHandle; |
|
1509 }; |
|
1510 |
|
1511 static already_AddRefed<nsINodeInfo> nullNodeInfo(nullptr); |
|
1512 |
|
1513 // ================================================================== |
|
1514 // = |
|
1515 // ================================================================== |
|
1516 nsIDocument::nsIDocument() |
|
1517 : nsINode(nullNodeInfo), |
|
1518 mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")), |
|
1519 mNodeInfoManager(nullptr), |
|
1520 mCompatMode(eCompatibility_FullStandards), |
|
1521 mVisibilityState(dom::VisibilityState::Hidden), |
|
1522 mIsInitialDocumentInWindow(false), |
|
1523 mMayStartLayout(true), |
|
1524 mVisible(true), |
|
1525 mRemovedFromDocShell(false), |
|
1526 // mAllowDNSPrefetch starts true, so that we can always reliably && it |
|
1527 // with various values that might disable it. Since we never prefetch |
|
1528 // unless we get a window, and in that case the docshell value will get |
|
1529 // &&-ed in, this is safe. |
|
1530 mAllowDNSPrefetch(true), |
|
1531 mIsBeingUsedAsImage(false), |
|
1532 mHasLinksToUpdate(false), |
|
1533 mPartID(0), |
|
1534 mDidFireDOMContentLoaded(true) |
|
1535 { |
|
1536 SetInDocument(); |
|
1537 } |
|
1538 |
|
1539 // NOTE! nsDocument::operator new() zeroes out all members, so don't |
|
1540 // bother initializing members to 0. |
|
1541 |
|
1542 nsDocument::nsDocument(const char* aContentType) |
|
1543 : nsIDocument() |
|
1544 , mAnimatingImages(true) |
|
1545 , mViewportType(Unknown) |
|
1546 { |
|
1547 SetContentTypeInternal(nsDependentCString(aContentType)); |
|
1548 |
|
1549 #ifdef PR_LOGGING |
|
1550 if (!gDocumentLeakPRLog) |
|
1551 gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak"); |
|
1552 |
|
1553 if (gDocumentLeakPRLog) |
|
1554 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, |
|
1555 ("DOCUMENT %p created", this)); |
|
1556 |
|
1557 if (!gCspPRLog) |
|
1558 gCspPRLog = PR_NewLogModule("CSP"); |
|
1559 #endif |
|
1560 |
|
1561 // Start out mLastStyleSheetSet as null, per spec |
|
1562 SetDOMStringToNull(mLastStyleSheetSet); |
|
1563 |
|
1564 if (sProcessingStack.empty()) { |
|
1565 sProcessingStack.construct(); |
|
1566 // Add the base queue sentinel to the processing stack. |
|
1567 sProcessingStack.ref().AppendElement((CustomElementData*) nullptr); |
|
1568 } |
|
1569 } |
|
1570 |
|
1571 static PLDHashOperator |
|
1572 ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg) |
|
1573 { |
|
1574 if (aBoxObject) { |
|
1575 aBoxObject->Clear(); |
|
1576 } |
|
1577 return PL_DHASH_NEXT; |
|
1578 } |
|
1579 |
|
1580 nsIDocument::~nsIDocument() |
|
1581 { |
|
1582 if (mNodeInfoManager) { |
|
1583 mNodeInfoManager->DropDocumentReference(); |
|
1584 } |
|
1585 } |
|
1586 |
|
1587 |
|
1588 nsDocument::~nsDocument() |
|
1589 { |
|
1590 #ifdef PR_LOGGING |
|
1591 if (gDocumentLeakPRLog) |
|
1592 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, |
|
1593 ("DOCUMENT %p destroyed", this)); |
|
1594 #endif |
|
1595 |
|
1596 NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document"); |
|
1597 |
|
1598 if (IsTopLevelContentDocument()) { |
|
1599 //don't report for about: pages |
|
1600 nsCOMPtr<nsIPrincipal> principal = GetPrincipal(); |
|
1601 nsCOMPtr<nsIURI> uri; |
|
1602 principal->GetURI(getter_AddRefs(uri)); |
|
1603 bool isAboutScheme = true; |
|
1604 if (uri) { |
|
1605 uri->SchemeIs("about", &isAboutScheme); |
|
1606 } |
|
1607 |
|
1608 if (!isAboutScheme) { |
|
1609 // Record the page load |
|
1610 uint32_t pageLoaded = 1; |
|
1611 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded); |
|
1612 // Record the mixed content status of the docshell in Telemetry |
|
1613 enum { |
|
1614 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page |
|
1615 MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content |
|
1616 MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content |
|
1617 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content |
|
1618 }; |
|
1619 |
|
1620 bool mixedActiveLoaded = GetHasMixedActiveContentLoaded(); |
|
1621 bool mixedActiveBlocked = GetHasMixedActiveContentBlocked(); |
|
1622 |
|
1623 bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded(); |
|
1624 bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked(); |
|
1625 |
|
1626 bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded); |
|
1627 bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded); |
|
1628 |
|
1629 uint32_t mixedContentLevel = NO_MIXED_CONTENT; |
|
1630 if (hasMixedDisplay && hasMixedActive) { |
|
1631 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT; |
|
1632 } else if (hasMixedActive){ |
|
1633 mixedContentLevel = MIXED_ACTIVE_CONTENT; |
|
1634 } else if (hasMixedDisplay) { |
|
1635 mixedContentLevel = MIXED_DISPLAY_CONTENT; |
|
1636 } |
|
1637 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel); |
|
1638 } |
|
1639 } |
|
1640 |
|
1641 mInDestructor = true; |
|
1642 mInUnlinkOrDeletion = true; |
|
1643 |
|
1644 mRegistry = nullptr; |
|
1645 |
|
1646 mozilla::DropJSObjects(this); |
|
1647 |
|
1648 // Clear mObservers to keep it in sync with the mutationobserver list |
|
1649 mObservers.Clear(); |
|
1650 |
|
1651 if (mStyleSheetSetList) { |
|
1652 mStyleSheetSetList->Disconnect(); |
|
1653 } |
|
1654 |
|
1655 if (mAnimationController) { |
|
1656 mAnimationController->Disconnect(); |
|
1657 } |
|
1658 |
|
1659 mParentDocument = nullptr; |
|
1660 |
|
1661 // Kill the subdocument map, doing this will release its strong |
|
1662 // references, if any. |
|
1663 if (mSubDocuments) { |
|
1664 PL_DHashTableDestroy(mSubDocuments); |
|
1665 |
|
1666 mSubDocuments = nullptr; |
|
1667 } |
|
1668 |
|
1669 // Destroy link map now so we don't waste time removing |
|
1670 // links one by one |
|
1671 DestroyElementMaps(); |
|
1672 |
|
1673 nsAutoScriptBlocker scriptBlocker; |
|
1674 |
|
1675 int32_t indx; // must be signed |
|
1676 uint32_t count = mChildren.ChildCount(); |
|
1677 for (indx = int32_t(count) - 1; indx >= 0; --indx) { |
|
1678 mChildren.ChildAt(indx)->UnbindFromTree(); |
|
1679 mChildren.RemoveChildAt(indx); |
|
1680 } |
|
1681 mFirstChild = nullptr; |
|
1682 mCachedRootElement = nullptr; |
|
1683 |
|
1684 // Let the stylesheets know we're going away |
|
1685 indx = mStyleSheets.Count(); |
|
1686 while (--indx >= 0) { |
|
1687 mStyleSheets[indx]->SetOwningDocument(nullptr); |
|
1688 } |
|
1689 indx = mCatalogSheets.Count(); |
|
1690 while (--indx >= 0) { |
|
1691 static_cast<nsCSSStyleSheet*>(mCatalogSheets[indx])->SetOwningNode(nullptr); |
|
1692 mCatalogSheets[indx]->SetOwningDocument(nullptr); |
|
1693 } |
|
1694 if (mAttrStyleSheet) { |
|
1695 mAttrStyleSheet->SetOwningDocument(nullptr); |
|
1696 } |
|
1697 |
|
1698 if (mListenerManager) { |
|
1699 mListenerManager->Disconnect(); |
|
1700 UnsetFlags(NODE_HAS_LISTENERMANAGER); |
|
1701 } |
|
1702 |
|
1703 if (mScriptLoader) { |
|
1704 mScriptLoader->DropDocumentReference(); |
|
1705 } |
|
1706 |
|
1707 if (mCSSLoader) { |
|
1708 // Could be null here if Init() failed |
|
1709 mCSSLoader->DropDocumentReference(); |
|
1710 } |
|
1711 |
|
1712 if (mStyleImageLoader) { |
|
1713 mStyleImageLoader->DropDocumentReference(); |
|
1714 } |
|
1715 |
|
1716 delete mHeaderData; |
|
1717 |
|
1718 if (mBoxObjectTable) { |
|
1719 mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr); |
|
1720 delete mBoxObjectTable; |
|
1721 } |
|
1722 |
|
1723 mPendingTitleChangeEvent.Revoke(); |
|
1724 |
|
1725 for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) { |
|
1726 nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]); |
|
1727 } |
|
1728 |
|
1729 // We don't want to leave residual locks on images. Make sure we're in an |
|
1730 // unlocked state, and then clear the table. |
|
1731 SetImageLockingState(false); |
|
1732 mImageTracker.Clear(); |
|
1733 |
|
1734 mPlugins.Clear(); |
|
1735 } |
|
1736 |
|
1737 NS_INTERFACE_TABLE_HEAD(nsDocument) |
|
1738 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
1739 NS_INTERFACE_TABLE_BEGIN |
|
1740 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode) |
|
1741 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode) |
|
1742 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument) |
|
1743 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument) |
|
1744 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode) |
|
1745 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL) |
|
1746 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal) |
|
1747 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget) |
|
1748 NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget) |
|
1749 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference) |
|
1750 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer) |
|
1751 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver) |
|
1752 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer) |
|
1753 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver) |
|
1754 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator) |
|
1755 NS_INTERFACE_TABLE_END |
|
1756 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument) |
|
1757 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver, |
|
1758 new nsNode3Tearoff(this)) |
|
1759 NS_INTERFACE_MAP_END |
|
1760 |
|
1761 |
|
1762 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument) |
|
1763 NS_IMETHODIMP_(MozExternalRefCountType) |
|
1764 nsDocument::Release() |
|
1765 { |
|
1766 NS_PRECONDITION(0 != mRefCnt, "dup release"); |
|
1767 NS_ASSERT_OWNINGTHREAD(nsDocument); |
|
1768 nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this); |
|
1769 bool shouldDelete = false; |
|
1770 nsrefcnt count = mRefCnt.decr(base, &shouldDelete); |
|
1771 NS_LOG_RELEASE(this, count, "nsDocument"); |
|
1772 if (count == 0) { |
|
1773 if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) { |
|
1774 mNeedsReleaseAfterStackRefCntRelease = true; |
|
1775 NS_ADDREF_THIS(); |
|
1776 return mRefCnt.get(); |
|
1777 } |
|
1778 mRefCnt.incr(base); |
|
1779 nsNodeUtils::LastRelease(this); |
|
1780 mRefCnt.decr(base); |
|
1781 if (shouldDelete) { |
|
1782 mRefCnt.stabilizeForDeletion(); |
|
1783 DeleteCycleCollectable(); |
|
1784 } |
|
1785 } |
|
1786 return count; |
|
1787 } |
|
1788 |
|
1789 NS_IMETHODIMP_(void) |
|
1790 nsDocument::DeleteCycleCollectable() |
|
1791 { |
|
1792 delete this; |
|
1793 } |
|
1794 |
|
1795 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument) |
|
1796 if (Element::CanSkip(tmp, aRemovingAllowed)) { |
|
1797 EventListenerManager* elm = tmp->GetExistingListenerManager(); |
|
1798 if (elm) { |
|
1799 elm->MarkForCC(); |
|
1800 } |
|
1801 return true; |
|
1802 } |
|
1803 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
|
1804 |
|
1805 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument) |
|
1806 return Element::CanSkipInCC(tmp); |
|
1807 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
|
1808 |
|
1809 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument) |
|
1810 return Element::CanSkipThis(tmp); |
|
1811 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
|
1812 |
|
1813 static PLDHashOperator |
|
1814 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, |
|
1815 void *arg) |
|
1816 { |
|
1817 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
|
1818 nsCycleCollectionTraversalCallback *cb = |
|
1819 static_cast<nsCycleCollectionTraversalCallback*>(arg); |
|
1820 |
|
1821 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey"); |
|
1822 cb->NoteXPCOMChild(entry->mKey); |
|
1823 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument"); |
|
1824 cb->NoteXPCOMChild(entry->mSubDocument); |
|
1825 |
|
1826 return PL_DHASH_NEXT; |
|
1827 } |
|
1828 |
|
1829 static PLDHashOperator |
|
1830 RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData, |
|
1831 void* aClosure) |
|
1832 { |
|
1833 nsCycleCollectionTraversalCallback *cb = |
|
1834 static_cast<nsCycleCollectionTraversalCallback*>(aClosure); |
|
1835 |
|
1836 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
1837 "mRadioGroups entry->mSelectedRadioButton"); |
|
1838 cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton)); |
|
1839 |
|
1840 uint32_t i, count = aData->mRadioButtons.Count(); |
|
1841 for (i = 0; i < count; ++i) { |
|
1842 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
|
1843 "mRadioGroups entry->mRadioButtons[i]"); |
|
1844 cb->NoteXPCOMChild(aData->mRadioButtons[i]); |
|
1845 } |
|
1846 |
|
1847 return PL_DHASH_NEXT; |
|
1848 } |
|
1849 |
|
1850 static PLDHashOperator |
|
1851 BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg) |
|
1852 { |
|
1853 nsCycleCollectionTraversalCallback *cb = |
|
1854 static_cast<nsCycleCollectionTraversalCallback*>(userArg); |
|
1855 |
|
1856 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry"); |
|
1857 cb->NoteXPCOMChild(boxObject); |
|
1858 |
|
1859 return PL_DHASH_NEXT; |
|
1860 } |
|
1861 |
|
1862 static PLDHashOperator |
|
1863 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg) |
|
1864 { |
|
1865 nsCycleCollectionTraversalCallback *cb = |
|
1866 static_cast<nsCycleCollectionTraversalCallback*>(aArg); |
|
1867 aEntry->Traverse(cb); |
|
1868 return PL_DHASH_NEXT; |
|
1869 } |
|
1870 |
|
1871 static const char* kNSURIs[] = { |
|
1872 "([none])", |
|
1873 "(xmlns)", |
|
1874 "(xml)", |
|
1875 "(xhtml)", |
|
1876 "(XLink)", |
|
1877 "(XSLT)", |
|
1878 "(XBL)", |
|
1879 "(MathML)", |
|
1880 "(RDF)", |
|
1881 "(XUL)" |
|
1882 }; |
|
1883 |
|
1884 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) |
|
1885 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
|
1886 char name[512]; |
|
1887 nsAutoCString loadedAsData; |
|
1888 if (tmp->IsLoadedAsData()) { |
|
1889 loadedAsData.AssignLiteral("data"); |
|
1890 } else { |
|
1891 loadedAsData.AssignLiteral("normal"); |
|
1892 } |
|
1893 uint32_t nsid = tmp->GetDefaultNamespaceID(); |
|
1894 nsAutoCString uri; |
|
1895 if (tmp->mDocumentURI) |
|
1896 tmp->mDocumentURI->GetSpec(uri); |
|
1897 if (nsid < ArrayLength(kNSURIs)) { |
|
1898 PR_snprintf(name, sizeof(name), "nsDocument %s %s %s", |
|
1899 loadedAsData.get(), kNSURIs[nsid], uri.get()); |
|
1900 } |
|
1901 else { |
|
1902 PR_snprintf(name, sizeof(name), "nsDocument %s %s", |
|
1903 loadedAsData.get(), uri.get()); |
|
1904 } |
|
1905 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
|
1906 } |
|
1907 else { |
|
1908 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get()) |
|
1909 } |
|
1910 |
|
1911 // Always need to traverse script objects, so do that before we check |
|
1912 // if we're uncollectable. |
|
1913 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
1914 |
|
1915 if (!nsINode::Traverse(tmp, cb)) { |
|
1916 return NS_SUCCESS_INTERRUPTED_TRAVERSE; |
|
1917 } |
|
1918 |
|
1919 tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb); |
|
1920 |
|
1921 tmp->mExternalResourceMap.Traverse(&cb); |
|
1922 |
|
1923 // Traverse the mChildren nsAttrAndChildArray. |
|
1924 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) { |
|
1925 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]"); |
|
1926 cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1)); |
|
1927 } |
|
1928 |
|
1929 // Traverse all nsIDocument pointer members. |
|
1930 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo) |
|
1931 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument) |
|
1932 |
|
1933 // Traverse all nsDocument nsCOMPtrs. |
|
1934 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) |
|
1935 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject) |
|
1936 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) |
|
1937 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets) |
|
1938 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList) |
|
1939 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) |
|
1940 |
|
1941 tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb); |
|
1942 |
|
1943 // The boxobject for an element will only exist as long as it's in the |
|
1944 // document, so we'll traverse the table here instead of from the element. |
|
1945 if (tmp->mBoxObjectTable) { |
|
1946 tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb); |
|
1947 } |
|
1948 |
|
1949 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel) |
|
1950 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet) |
|
1951 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator) |
|
1952 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState) |
|
1953 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker) |
|
1954 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref) |
|
1955 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) |
|
1956 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) |
|
1957 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument) |
|
1958 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder) |
|
1959 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached) |
|
1960 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager) |
|
1961 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner) |
|
1962 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection) |
|
1963 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry) |
|
1964 |
|
1965 // Traverse all our nsCOMArrays. |
|
1966 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets) |
|
1967 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCatalogSheets) |
|
1968 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages) |
|
1969 |
|
1970 for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) { |
|
1971 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]"); |
|
1972 cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports()); |
|
1973 } |
|
1974 |
|
1975 // Traverse animation components |
|
1976 if (tmp->mAnimationController) { |
|
1977 tmp->mAnimationController->Traverse(&cb); |
|
1978 } |
|
1979 |
|
1980 if (tmp->mSubDocuments && tmp->mSubDocuments->ops) { |
|
1981 PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb); |
|
1982 } |
|
1983 |
|
1984 if (tmp->mCSSLoader) { |
|
1985 tmp->mCSSLoader->TraverseCachedSheets(cb); |
|
1986 } |
|
1987 |
|
1988 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) { |
|
1989 nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb); |
|
1990 } |
|
1991 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
1992 |
|
1993 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) |
|
1994 |
|
1995 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument) |
|
1996 if (tmp->PreservingWrapper()) { |
|
1997 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando); |
|
1998 } |
|
1999 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
|
2000 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
2001 |
|
2002 |
|
2003 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) |
|
2004 tmp->mInUnlinkOrDeletion = true; |
|
2005 |
|
2006 // Clear out our external resources |
|
2007 tmp->mExternalResourceMap.Shutdown(); |
|
2008 |
|
2009 nsAutoScriptBlocker scriptBlocker; |
|
2010 |
|
2011 nsINode::Unlink(tmp); |
|
2012 |
|
2013 // Unlink the mChildren nsAttrAndChildArray. |
|
2014 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1; |
|
2015 indx >= 0; --indx) { |
|
2016 tmp->mChildren.ChildAt(indx)->UnbindFromTree(); |
|
2017 tmp->mChildren.RemoveChildAt(indx); |
|
2018 } |
|
2019 tmp->mFirstChild = nullptr; |
|
2020 |
|
2021 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator) |
|
2022 tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer |
|
2023 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) |
|
2024 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref) |
|
2025 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation) |
|
2026 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps) |
|
2027 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument) |
|
2028 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder) |
|
2029 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager) |
|
2030 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner) |
|
2031 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection) |
|
2032 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry) |
|
2033 |
|
2034 tmp->mParentDocument = nullptr; |
|
2035 |
|
2036 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages) |
|
2037 |
|
2038 |
|
2039 if (tmp->mBoxObjectTable) { |
|
2040 tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr); |
|
2041 delete tmp->mBoxObjectTable; |
|
2042 tmp->mBoxObjectTable = nullptr; |
|
2043 } |
|
2044 |
|
2045 if (tmp->mListenerManager) { |
|
2046 tmp->mListenerManager->Disconnect(); |
|
2047 tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); |
|
2048 tmp->mListenerManager = nullptr; |
|
2049 } |
|
2050 |
|
2051 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets) |
|
2052 |
|
2053 if (tmp->mStyleSheetSetList) { |
|
2054 tmp->mStyleSheetSetList->Disconnect(); |
|
2055 tmp->mStyleSheetSetList = nullptr; |
|
2056 } |
|
2057 |
|
2058 if (tmp->mSubDocuments) { |
|
2059 PL_DHashTableDestroy(tmp->mSubDocuments); |
|
2060 tmp->mSubDocuments = nullptr; |
|
2061 } |
|
2062 |
|
2063 tmp->mFrameRequestCallbacks.Clear(); |
|
2064 |
|
2065 tmp->mRadioGroups.Clear(); |
|
2066 |
|
2067 // nsDocument has a pretty complex destructor, so we're going to |
|
2068 // assume that *most* cycles you actually want to break somewhere |
|
2069 // else, and not unlink an awful lot here. |
|
2070 |
|
2071 tmp->mIdentifierMap.Clear(); |
|
2072 tmp->mExpandoAndGeneration.Unlink(); |
|
2073 |
|
2074 if (tmp->mAnimationController) { |
|
2075 tmp->mAnimationController->Unlink(); |
|
2076 } |
|
2077 |
|
2078 tmp->mPendingTitleChangeEvent.Revoke(); |
|
2079 |
|
2080 if (tmp->mCSSLoader) { |
|
2081 tmp->mCSSLoader->UnlinkCachedSheets(); |
|
2082 } |
|
2083 |
|
2084 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) { |
|
2085 nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]); |
|
2086 } |
|
2087 |
|
2088 tmp->mInUnlinkOrDeletion = false; |
|
2089 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
2090 |
|
2091 static bool sPrefsInitialized = false; |
|
2092 static uint32_t sOnloadDecodeLimit = 0; |
|
2093 |
|
2094 nsresult |
|
2095 nsDocument::Init() |
|
2096 { |
|
2097 if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) { |
|
2098 return NS_ERROR_ALREADY_INITIALIZED; |
|
2099 } |
|
2100 |
|
2101 if (!sPrefsInitialized) { |
|
2102 sPrefsInitialized = true; |
|
2103 Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0); |
|
2104 } |
|
2105 |
|
2106 // Force initialization. |
|
2107 nsINode::nsSlots* slots = Slots(); |
|
2108 |
|
2109 // Prepend self as mutation-observer whether we need it or not (some |
|
2110 // subclasses currently do, other don't). This is because the code in |
|
2111 // nsNodeUtils always notifies the first observer first, expecting the |
|
2112 // first observer to be the document. |
|
2113 NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)), |
|
2114 NS_ERROR_OUT_OF_MEMORY); |
|
2115 |
|
2116 |
|
2117 mOnloadBlocker = new nsOnloadBlocker(); |
|
2118 mCSSLoader = new mozilla::css::Loader(this); |
|
2119 // Assume we're not quirky, until we know otherwise |
|
2120 mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards); |
|
2121 |
|
2122 mStyleImageLoader = new mozilla::css::ImageLoader(this); |
|
2123 |
|
2124 mNodeInfoManager = new nsNodeInfoManager(); |
|
2125 nsresult rv = mNodeInfoManager->Init(this); |
|
2126 NS_ENSURE_SUCCESS(rv, rv); |
|
2127 |
|
2128 // mNodeInfo keeps NodeInfoManager alive! |
|
2129 mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo(); |
|
2130 NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY); |
|
2131 NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE, |
|
2132 "Bad NodeType in aNodeInfo"); |
|
2133 |
|
2134 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!"); |
|
2135 |
|
2136 // If after creation the owner js global is not set for a document |
|
2137 // we use the default compartment for this document, instead of creating |
|
2138 // wrapper in some random compartment when the document is exposed to js |
|
2139 // via some events. |
|
2140 nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal(); |
|
2141 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); |
|
2142 mScopeObject = do_GetWeakReference(global); |
|
2143 MOZ_ASSERT(mScopeObject); |
|
2144 |
|
2145 mScriptLoader = new nsScriptLoader(this); |
|
2146 |
|
2147 mozilla::HoldJSObjects(this); |
|
2148 |
|
2149 return NS_OK; |
|
2150 } |
|
2151 |
|
2152 void |
|
2153 nsIDocument::DeleteAllProperties() |
|
2154 { |
|
2155 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) { |
|
2156 PropertyTable(i)->DeleteAllProperties(); |
|
2157 } |
|
2158 } |
|
2159 |
|
2160 void |
|
2161 nsIDocument::DeleteAllPropertiesFor(nsINode* aNode) |
|
2162 { |
|
2163 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) { |
|
2164 PropertyTable(i)->DeleteAllPropertiesFor(aNode); |
|
2165 } |
|
2166 } |
|
2167 |
|
2168 nsPropertyTable* |
|
2169 nsIDocument::GetExtraPropertyTable(uint16_t aCategory) |
|
2170 { |
|
2171 NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled"); |
|
2172 while (aCategory >= mExtraPropertyTables.Length() + 1) { |
|
2173 mExtraPropertyTables.AppendElement(new nsPropertyTable()); |
|
2174 } |
|
2175 return mExtraPropertyTables[aCategory - 1]; |
|
2176 } |
|
2177 |
|
2178 bool |
|
2179 nsIDocument::IsVisibleConsideringAncestors() const |
|
2180 { |
|
2181 const nsIDocument *parent = this; |
|
2182 do { |
|
2183 if (!parent->IsVisible()) { |
|
2184 return false; |
|
2185 } |
|
2186 } while ((parent = parent->GetParentDocument())); |
|
2187 |
|
2188 return true; |
|
2189 } |
|
2190 |
|
2191 void |
|
2192 nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) |
|
2193 { |
|
2194 nsCOMPtr<nsIURI> uri; |
|
2195 nsCOMPtr<nsIPrincipal> principal; |
|
2196 if (aChannel) { |
|
2197 // Note: this code is duplicated in XULDocument::StartDocumentLoad and |
|
2198 // nsScriptSecurityManager::GetChannelPrincipal. |
|
2199 // Note: this should match nsDocShell::OnLoadingSite |
|
2200 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); |
|
2201 |
|
2202 nsIScriptSecurityManager *securityManager = |
|
2203 nsContentUtils::GetSecurityManager(); |
|
2204 if (securityManager) { |
|
2205 securityManager->GetChannelPrincipal(aChannel, |
|
2206 getter_AddRefs(principal)); |
|
2207 } |
|
2208 } |
|
2209 |
|
2210 ResetToURI(uri, aLoadGroup, principal); |
|
2211 |
|
2212 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel); |
|
2213 if (bag) { |
|
2214 nsCOMPtr<nsIURI> baseURI; |
|
2215 bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), |
|
2216 NS_GET_IID(nsIURI), getter_AddRefs(baseURI)); |
|
2217 if (baseURI) { |
|
2218 mDocumentBaseURI = baseURI; |
|
2219 mChromeXHRDocBaseURI = baseURI; |
|
2220 } |
|
2221 } |
|
2222 |
|
2223 mChannel = aChannel; |
|
2224 } |
|
2225 |
|
2226 void |
|
2227 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, |
|
2228 nsIPrincipal* aPrincipal) |
|
2229 { |
|
2230 NS_PRECONDITION(aURI, "Null URI passed to ResetToURI"); |
|
2231 |
|
2232 #ifdef PR_LOGGING |
|
2233 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { |
|
2234 nsAutoCString spec; |
|
2235 aURI->GetSpec(spec); |
|
2236 PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get()); |
|
2237 } |
|
2238 #endif |
|
2239 |
|
2240 mSecurityInfo = nullptr; |
|
2241 |
|
2242 mDocumentLoadGroup = nullptr; |
|
2243 |
|
2244 // Delete references to sub-documents and kill the subdocument map, |
|
2245 // if any. It holds strong references |
|
2246 if (mSubDocuments) { |
|
2247 PL_DHashTableDestroy(mSubDocuments); |
|
2248 |
|
2249 mSubDocuments = nullptr; |
|
2250 } |
|
2251 |
|
2252 // Destroy link map now so we don't waste time removing |
|
2253 // links one by one |
|
2254 DestroyElementMaps(); |
|
2255 |
|
2256 bool oldVal = mInUnlinkOrDeletion; |
|
2257 mInUnlinkOrDeletion = true; |
|
2258 uint32_t count = mChildren.ChildCount(); |
|
2259 { // Scope for update |
|
2260 MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true); |
|
2261 for (int32_t i = int32_t(count) - 1; i >= 0; i--) { |
|
2262 nsCOMPtr<nsIContent> content = mChildren.ChildAt(i); |
|
2263 |
|
2264 nsIContent* previousSibling = content->GetPreviousSibling(); |
|
2265 |
|
2266 if (nsINode::GetFirstChild() == content) { |
|
2267 mFirstChild = content->GetNextSibling(); |
|
2268 } |
|
2269 mChildren.RemoveChildAt(i); |
|
2270 nsNodeUtils::ContentRemoved(this, content, i, previousSibling); |
|
2271 content->UnbindFromTree(); |
|
2272 } |
|
2273 mCachedRootElement = nullptr; |
|
2274 } |
|
2275 mInUnlinkOrDeletion = oldVal; |
|
2276 |
|
2277 mRegistry = nullptr; |
|
2278 |
|
2279 // Reset our stylesheets |
|
2280 ResetStylesheetsToURI(aURI); |
|
2281 |
|
2282 // Release the listener manager |
|
2283 if (mListenerManager) { |
|
2284 mListenerManager->Disconnect(); |
|
2285 mListenerManager = nullptr; |
|
2286 } |
|
2287 |
|
2288 // Release the stylesheets list. |
|
2289 mDOMStyleSheets = nullptr; |
|
2290 |
|
2291 // Release our principal after tearing down the document, rather than before. |
|
2292 // This ensures that, during teardown, the document and the dying window (which |
|
2293 // already nulled out its document pointer and cached the principal) have |
|
2294 // matching principals. |
|
2295 SetPrincipal(nullptr); |
|
2296 |
|
2297 // Clear the original URI so SetDocumentURI sets it. |
|
2298 mOriginalURI = nullptr; |
|
2299 |
|
2300 SetDocumentURI(aURI); |
|
2301 mChromeXHRDocURI = aURI; |
|
2302 // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns |
|
2303 // mDocumentURI. |
|
2304 mDocumentBaseURI = nullptr; |
|
2305 mChromeXHRDocBaseURI = nullptr; |
|
2306 |
|
2307 if (aLoadGroup) { |
|
2308 mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); |
|
2309 // there was an assertion here that aLoadGroup was not null. This |
|
2310 // is no longer valid: nsDocShell::SetDocument does not create a |
|
2311 // load group, and it works just fine |
|
2312 |
|
2313 // XXXbz what does "just fine" mean exactly? And given that there |
|
2314 // is no nsDocShell::SetDocument, what is this talking about? |
|
2315 } |
|
2316 |
|
2317 mLastModified.Truncate(); |
|
2318 // XXXbz I guess we're assuming that the caller will either pass in |
|
2319 // a channel with a useful type or call SetContentType? |
|
2320 SetContentTypeInternal(EmptyCString()); |
|
2321 mContentLanguage.Truncate(); |
|
2322 mBaseTarget.Truncate(); |
|
2323 mReferrer.Truncate(); |
|
2324 |
|
2325 mXMLDeclarationBits = 0; |
|
2326 |
|
2327 // Now get our new principal |
|
2328 if (aPrincipal) { |
|
2329 SetPrincipal(aPrincipal); |
|
2330 } else { |
|
2331 nsIScriptSecurityManager *securityManager = |
|
2332 nsContentUtils::GetSecurityManager(); |
|
2333 if (securityManager) { |
|
2334 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
2335 |
|
2336 if (!docShell && aLoadGroup) { |
|
2337 nsCOMPtr<nsIInterfaceRequestor> cbs; |
|
2338 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs)); |
|
2339 docShell = do_GetInterface(cbs); |
|
2340 } |
|
2341 |
|
2342 MOZ_ASSERT(docShell, |
|
2343 "must be in a docshell or pass in an explicit principal"); |
|
2344 |
|
2345 nsCOMPtr<nsIPrincipal> principal; |
|
2346 nsresult rv = securityManager-> |
|
2347 GetDocShellCodebasePrincipal(mDocumentURI, docShell, |
|
2348 getter_AddRefs(principal)); |
|
2349 if (NS_SUCCEEDED(rv)) { |
|
2350 SetPrincipal(principal); |
|
2351 } |
|
2352 } |
|
2353 } |
|
2354 |
|
2355 // Refresh the principal on the compartment. |
|
2356 nsPIDOMWindow* win = GetInnerWindow(); |
|
2357 if (win) { |
|
2358 win->RefreshCompartmentPrincipal(); |
|
2359 } |
|
2360 } |
|
2361 |
|
2362 void |
|
2363 nsDocument::RemoveDocStyleSheetsFromStyleSets() |
|
2364 { |
|
2365 // The stylesheets should forget us |
|
2366 int32_t indx = mStyleSheets.Count(); |
|
2367 while (--indx >= 0) { |
|
2368 nsIStyleSheet* sheet = mStyleSheets[indx]; |
|
2369 sheet->SetOwningDocument(nullptr); |
|
2370 |
|
2371 if (sheet->IsApplicable()) { |
|
2372 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
2373 if (shell) { |
|
2374 shell->StyleSet()->RemoveDocStyleSheet(sheet); |
|
2375 } |
|
2376 } |
|
2377 // XXX Tell observers? |
|
2378 } |
|
2379 } |
|
2380 |
|
2381 void |
|
2382 nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType) |
|
2383 { |
|
2384 // The stylesheets should forget us |
|
2385 int32_t indx = aSheets.Count(); |
|
2386 while (--indx >= 0) { |
|
2387 nsIStyleSheet* sheet = aSheets[indx]; |
|
2388 sheet->SetOwningDocument(nullptr); |
|
2389 |
|
2390 if (sheet->IsApplicable()) { |
|
2391 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
2392 if (shell) { |
|
2393 shell->StyleSet()->RemoveStyleSheet(aType, sheet); |
|
2394 } |
|
2395 } |
|
2396 |
|
2397 // XXX Tell observers? |
|
2398 } |
|
2399 |
|
2400 } |
|
2401 |
|
2402 void |
|
2403 nsDocument::ResetStylesheetsToURI(nsIURI* aURI) |
|
2404 { |
|
2405 MOZ_ASSERT(aURI); |
|
2406 |
|
2407 mozAutoDocUpdate upd(this, UPDATE_STYLE, true); |
|
2408 RemoveDocStyleSheetsFromStyleSets(); |
|
2409 RemoveStyleSheetsFromStyleSets(mCatalogSheets, nsStyleSet::eAgentSheet); |
|
2410 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet); |
|
2411 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet); |
|
2412 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet); |
|
2413 |
|
2414 // Release all the sheets |
|
2415 mStyleSheets.Clear(); |
|
2416 for (uint32_t i = 0; i < SheetTypeCount; ++i) |
|
2417 mAdditionalSheets[i].Clear(); |
|
2418 |
|
2419 // NOTE: We don't release the catalog sheets. It doesn't really matter |
|
2420 // now, but it could in the future -- in which case not releasing them |
|
2421 // is probably the right thing to do. |
|
2422 |
|
2423 // Now reset our inline style and attribute sheets. |
|
2424 if (mAttrStyleSheet) { |
|
2425 mAttrStyleSheet->Reset(); |
|
2426 mAttrStyleSheet->SetOwningDocument(this); |
|
2427 } else { |
|
2428 mAttrStyleSheet = new nsHTMLStyleSheet(this); |
|
2429 } |
|
2430 |
|
2431 if (!mStyleAttrStyleSheet) { |
|
2432 mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet(); |
|
2433 } |
|
2434 |
|
2435 // Now set up our style sets |
|
2436 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
2437 if (shell) { |
|
2438 FillStyleSet(shell->StyleSet()); |
|
2439 } |
|
2440 } |
|
2441 |
|
2442 static bool |
|
2443 AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData) |
|
2444 { |
|
2445 nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData); |
|
2446 styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet); |
|
2447 return true; |
|
2448 } |
|
2449 |
|
2450 static void |
|
2451 AppendSheetsToStyleSet(nsStyleSet* aStyleSet, |
|
2452 const nsCOMArray<nsIStyleSheet>& aSheets, |
|
2453 nsStyleSet::sheetType aType) |
|
2454 { |
|
2455 for (int32_t i = aSheets.Count() - 1; i >= 0; --i) { |
|
2456 aStyleSet->AppendStyleSheet(aType, aSheets[i]); |
|
2457 } |
|
2458 } |
|
2459 |
|
2460 |
|
2461 void |
|
2462 nsDocument::FillStyleSet(nsStyleSet* aStyleSet) |
|
2463 { |
|
2464 NS_PRECONDITION(aStyleSet, "Must have a style set"); |
|
2465 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0, |
|
2466 "Style set already has document sheets?"); |
|
2467 |
|
2468 // We could consider moving this to nsStyleSet::Init, to match its |
|
2469 // handling of the eAnimationSheet and eTransitionSheet levels. |
|
2470 aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet); |
|
2471 aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet); |
|
2472 |
|
2473 int32_t i; |
|
2474 for (i = mStyleSheets.Count() - 1; i >= 0; --i) { |
|
2475 nsIStyleSheet* sheet = mStyleSheets[i]; |
|
2476 if (sheet->IsApplicable()) { |
|
2477 aStyleSet->AddDocStyleSheet(sheet, this); |
|
2478 } |
|
2479 } |
|
2480 |
|
2481 nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); |
|
2482 if (sheetService) { |
|
2483 sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet, |
|
2484 aStyleSet); |
|
2485 } |
|
2486 |
|
2487 for (i = mCatalogSheets.Count() - 1; i >= 0; --i) { |
|
2488 nsIStyleSheet* sheet = mCatalogSheets[i]; |
|
2489 if (sheet->IsApplicable()) { |
|
2490 aStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet); |
|
2491 } |
|
2492 } |
|
2493 |
|
2494 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet], |
|
2495 nsStyleSet::eAgentSheet); |
|
2496 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet], |
|
2497 nsStyleSet::eUserSheet); |
|
2498 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet], |
|
2499 nsStyleSet::eDocSheet); |
|
2500 } |
|
2501 |
|
2502 nsresult |
|
2503 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, |
|
2504 nsILoadGroup* aLoadGroup, |
|
2505 nsISupports* aContainer, |
|
2506 nsIStreamListener **aDocListener, |
|
2507 bool aReset, nsIContentSink* aSink) |
|
2508 { |
|
2509 #ifdef PR_LOGGING |
|
2510 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { |
|
2511 nsCOMPtr<nsIURI> uri; |
|
2512 aChannel->GetURI(getter_AddRefs(uri)); |
|
2513 nsAutoCString spec; |
|
2514 if (uri) |
|
2515 uri->GetSpec(spec); |
|
2516 PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get()); |
|
2517 } |
|
2518 #endif |
|
2519 |
|
2520 #ifdef DEBUG |
|
2521 { |
|
2522 uint32_t appId; |
|
2523 nsresult rv = NodePrincipal()->GetAppId(&appId); |
|
2524 NS_ENSURE_SUCCESS(rv, rv); |
|
2525 MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID, |
|
2526 "Document should never have UNKNOWN_APP_ID"); |
|
2527 } |
|
2528 #endif |
|
2529 |
|
2530 MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED, |
|
2531 "Bad readyState"); |
|
2532 SetReadyStateInternal(READYSTATE_LOADING); |
|
2533 |
|
2534 if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) { |
|
2535 mLoadedAsData = true; |
|
2536 // We need to disable script & style loading in this case. |
|
2537 // We leave them disabled even in EndLoad(), and let anyone |
|
2538 // who puts the document on display to worry about enabling. |
|
2539 |
|
2540 // Do not load/process scripts when loading as data |
|
2541 ScriptLoader()->SetEnabled(false); |
|
2542 |
|
2543 // styles |
|
2544 CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data |
|
2545 } else if (nsCRT::strcmp("external-resource", aCommand) == 0) { |
|
2546 // Allow CSS, but not scripts |
|
2547 ScriptLoader()->SetEnabled(false); |
|
2548 } |
|
2549 |
|
2550 mMayStartLayout = false; |
|
2551 |
|
2552 mHaveInputEncoding = true; |
|
2553 |
|
2554 if (aReset) { |
|
2555 Reset(aChannel, aLoadGroup); |
|
2556 } |
|
2557 |
|
2558 nsAutoCString contentType; |
|
2559 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel); |
|
2560 if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString( |
|
2561 NS_LITERAL_STRING("contentType"), contentType))) || |
|
2562 NS_SUCCEEDED(aChannel->GetContentType(contentType))) { |
|
2563 // XXX this is only necessary for viewsource: |
|
2564 nsACString::const_iterator start, end, semicolon; |
|
2565 contentType.BeginReading(start); |
|
2566 contentType.EndReading(end); |
|
2567 semicolon = start; |
|
2568 FindCharInReadable(';', semicolon, end); |
|
2569 SetContentTypeInternal(Substring(start, semicolon)); |
|
2570 } |
|
2571 |
|
2572 RetrieveRelevantHeaders(aChannel); |
|
2573 |
|
2574 mChannel = aChannel; |
|
2575 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel); |
|
2576 if (inStrmChan) { |
|
2577 bool isSrcdocChannel; |
|
2578 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel); |
|
2579 if (isSrcdocChannel) { |
|
2580 mIsSrcdocDocument = true; |
|
2581 } |
|
2582 } |
|
2583 |
|
2584 // If this document is being loaded by a docshell, copy its sandbox flags |
|
2585 // to the document. These are immutable after being set here. |
|
2586 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer); |
|
2587 |
|
2588 if (docShell) { |
|
2589 nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags); |
|
2590 NS_ENSURE_SUCCESS(rv, rv); |
|
2591 } |
|
2592 |
|
2593 // If this is not a data document, set CSP. |
|
2594 if (!mLoadedAsData) { |
|
2595 nsresult rv = InitCSP(aChannel); |
|
2596 NS_ENSURE_SUCCESS(rv, rv); |
|
2597 } |
|
2598 |
|
2599 return NS_OK; |
|
2600 } |
|
2601 |
|
2602 void |
|
2603 CSPErrorQueue::Add(const char* aMessageName) |
|
2604 { |
|
2605 mErrors.AppendElement(aMessageName); |
|
2606 } |
|
2607 |
|
2608 void |
|
2609 CSPErrorQueue::Flush(nsIDocument* aDocument) |
|
2610 { |
|
2611 for (uint32_t i = 0; i < mErrors.Length(); i++) { |
|
2612 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
2613 NS_LITERAL_CSTRING("CSP"), aDocument, |
|
2614 nsContentUtils::eSECURITY_PROPERTIES, |
|
2615 mErrors[i]); |
|
2616 } |
|
2617 mErrors.Clear(); |
|
2618 } |
|
2619 |
|
2620 void |
|
2621 nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) |
|
2622 { |
|
2623 for (uint32_t i = 0; i < aMessages.Length(); ++i) { |
|
2624 nsAutoString messageTag; |
|
2625 aMessages[i]->GetTag(messageTag); |
|
2626 |
|
2627 nsAutoString category; |
|
2628 aMessages[i]->GetCategory(category); |
|
2629 |
|
2630 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
2631 NS_ConvertUTF16toUTF8(category), |
|
2632 this, nsContentUtils::eSECURITY_PROPERTIES, |
|
2633 NS_ConvertUTF16toUTF8(messageTag).get()); |
|
2634 } |
|
2635 } |
|
2636 |
|
2637 static nsresult |
|
2638 AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue, |
|
2639 nsIURI* aSelfURI, bool aReportOnly, bool aSpecCompliant) |
|
2640 { |
|
2641 // Need to tokenize the header value since multiple headers could be |
|
2642 // concatenated into one comma-separated list of policies. |
|
2643 // See RFC2616 section 4.2 (last paragraph) |
|
2644 nsresult rv = NS_OK; |
|
2645 nsCharSeparatedTokenizer tokenizer(aHeaderValue, ','); |
|
2646 while (tokenizer.hasMoreTokens()) { |
|
2647 const nsSubstring& policy = tokenizer.nextToken(); |
|
2648 rv = csp->AppendPolicy(policy, aSelfURI, aReportOnly, aSpecCompliant); |
|
2649 NS_ENSURE_SUCCESS(rv, rv); |
|
2650 #ifdef PR_LOGGING |
|
2651 { |
|
2652 PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
|
2653 ("CSP refined with policy: \"%s\"", |
|
2654 NS_ConvertUTF16toUTF8(policy).get())); |
|
2655 } |
|
2656 #endif |
|
2657 } |
|
2658 return NS_OK; |
|
2659 } |
|
2660 |
|
2661 nsresult |
|
2662 nsDocument::InitCSP(nsIChannel* aChannel) |
|
2663 { |
|
2664 nsCOMPtr<nsIContentSecurityPolicy> csp; |
|
2665 if (!CSPService::sCSPEnabled) { |
|
2666 #ifdef PR_LOGGING |
|
2667 PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
|
2668 ("CSP is disabled, skipping CSP init for document %p", this)); |
|
2669 #endif |
|
2670 return NS_OK; |
|
2671 } |
|
2672 |
|
2673 nsAutoCString tCspHeaderValue, tCspROHeaderValue; |
|
2674 nsAutoCString tCspOldHeaderValue, tCspOldROHeaderValue; |
|
2675 |
|
2676 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); |
|
2677 if (httpChannel) { |
|
2678 httpChannel->GetResponseHeader( |
|
2679 NS_LITERAL_CSTRING("x-content-security-policy"), |
|
2680 tCspOldHeaderValue); |
|
2681 |
|
2682 httpChannel->GetResponseHeader( |
|
2683 NS_LITERAL_CSTRING("x-content-security-policy-report-only"), |
|
2684 tCspOldROHeaderValue); |
|
2685 |
|
2686 httpChannel->GetResponseHeader( |
|
2687 NS_LITERAL_CSTRING("content-security-policy"), |
|
2688 tCspHeaderValue); |
|
2689 |
|
2690 httpChannel->GetResponseHeader( |
|
2691 NS_LITERAL_CSTRING("content-security-policy-report-only"), |
|
2692 tCspROHeaderValue); |
|
2693 } |
|
2694 NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue); |
|
2695 NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue); |
|
2696 NS_ConvertASCIItoUTF16 cspOldHeaderValue(tCspOldHeaderValue); |
|
2697 NS_ConvertASCIItoUTF16 cspOldROHeaderValue(tCspOldROHeaderValue); |
|
2698 |
|
2699 // Only use the CSP 1.0 spec compliant headers if a pref to do so |
|
2700 // is set. This lets us turn on the 1.0 parser per platform. This |
|
2701 // pref is also set by the tests for 1.0 spec compliant CSP. |
|
2702 bool specCompliantEnabled = |
|
2703 Preferences::GetBool("security.csp.speccompliant"); |
|
2704 |
|
2705 // If spec compliant pref isn't set, pretend we never got these headers. |
|
2706 if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) && |
|
2707 !specCompliantEnabled) { |
|
2708 PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
|
2709 ("Got spec compliant CSP headers but pref was not set")); |
|
2710 cspHeaderValue.Truncate(); |
|
2711 cspROHeaderValue.Truncate(); |
|
2712 } |
|
2713 |
|
2714 // If both the new header AND the old header are present, warn that |
|
2715 // the old header will be ignored. Otherwise, if the old header is |
|
2716 // present, warn that it will be deprecated. |
|
2717 bool oldHeaderIsPresent = !cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty(); |
|
2718 bool newHeaderIsPresent = !cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty(); |
|
2719 |
|
2720 if (oldHeaderIsPresent && newHeaderIsPresent) { |
|
2721 mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent"); |
|
2722 } else if (oldHeaderIsPresent) { |
|
2723 mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated"); |
|
2724 } |
|
2725 |
|
2726 // Figure out if we need to apply an app default CSP or a CSP from an app manifest |
|
2727 nsIPrincipal* principal = NodePrincipal(); |
|
2728 |
|
2729 uint16_t appStatus = principal->GetAppStatus(); |
|
2730 bool applyAppDefaultCSP = appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED || |
|
2731 appStatus == nsIPrincipal::APP_STATUS_CERTIFIED; |
|
2732 bool applyAppManifestCSP = false; |
|
2733 |
|
2734 nsAutoString appManifestCSP; |
|
2735 if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) { |
|
2736 nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); |
|
2737 if (appsService) { |
|
2738 uint32_t appId = 0; |
|
2739 if (NS_SUCCEEDED(principal->GetAppId(&appId))) { |
|
2740 appsService->GetCSPByLocalId(appId, appManifestCSP); |
|
2741 if (!appManifestCSP.IsEmpty()) { |
|
2742 applyAppManifestCSP = true; |
|
2743 } |
|
2744 } |
|
2745 } |
|
2746 } |
|
2747 |
|
2748 // If there's no CSP to apply, go ahead and return early |
|
2749 if (!applyAppDefaultCSP && |
|
2750 !applyAppManifestCSP && |
|
2751 cspHeaderValue.IsEmpty() && |
|
2752 cspROHeaderValue.IsEmpty() && |
|
2753 cspOldHeaderValue.IsEmpty() && |
|
2754 cspOldROHeaderValue.IsEmpty()) { |
|
2755 #ifdef PR_LOGGING |
|
2756 nsCOMPtr<nsIURI> chanURI; |
|
2757 aChannel->GetURI(getter_AddRefs(chanURI)); |
|
2758 nsAutoCString aspec; |
|
2759 chanURI->GetAsciiSpec(aspec); |
|
2760 PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
|
2761 ("no CSP for document, %s, %s", |
|
2762 aspec.get(), |
|
2763 applyAppDefaultCSP ? "is app" : "not an app")); |
|
2764 #endif |
|
2765 return NS_OK; |
|
2766 } |
|
2767 |
|
2768 #ifdef PR_LOGGING |
|
2769 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this)); |
|
2770 #endif |
|
2771 |
|
2772 nsresult rv; |
|
2773 |
|
2774 // If Document is an app check to see if we already set CSP and return early |
|
2775 // if that is indeed the case. |
|
2776 // |
|
2777 // In general (see bug 947831), we should not be setting CSP on a principal |
|
2778 // that aliases another document. For non-app code this is not a problem |
|
2779 // since we only share the underlying principal with nested browsing |
|
2780 // contexts for which a header cannot be set (e.g., about:blank and |
|
2781 // about:srcodoc iframes) and thus won't try to set the CSP again. This |
|
2782 // check ensures that we do not try to set CSP for an app. |
|
2783 if (applyAppDefaultCSP || applyAppManifestCSP) { |
|
2784 nsCOMPtr<nsIContentSecurityPolicy> csp; |
|
2785 rv = principal->GetCsp(getter_AddRefs(csp)); |
|
2786 NS_ENSURE_SUCCESS(rv, rv); |
|
2787 |
|
2788 if (csp) { |
|
2789 #ifdef PR_LOGGING |
|
2790 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s", |
|
2791 "This document is sharing principal with another document.", |
|
2792 "Since the document is an app, CSP was already set.", |
|
2793 "Skipping attempt to set CSP.")); |
|
2794 #endif |
|
2795 return NS_OK; |
|
2796 } |
|
2797 } |
|
2798 |
|
2799 // create new CSP object |
|
2800 csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv); |
|
2801 |
|
2802 if (NS_FAILED(rv)) { |
|
2803 #ifdef PR_LOGGING |
|
2804 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv)); |
|
2805 #endif |
|
2806 return rv; |
|
2807 } |
|
2808 |
|
2809 // used as a "self" identifier for the CSP. |
|
2810 nsCOMPtr<nsIURI> selfURI; |
|
2811 aChannel->GetURI(getter_AddRefs(selfURI)); |
|
2812 |
|
2813 // Store the request context for violation reports |
|
2814 csp->SetRequestContext(nullptr, nullptr, nullptr, aChannel); |
|
2815 |
|
2816 // ----- if the doc is an app and we want a default CSP, apply it. |
|
2817 if (applyAppDefaultCSP) { |
|
2818 nsAdoptingString appCSP; |
|
2819 if (appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) { |
|
2820 appCSP = Preferences::GetString("security.apps.privileged.CSP.default"); |
|
2821 NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default"); |
|
2822 } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) { |
|
2823 appCSP = Preferences::GetString("security.apps.certified.CSP.default"); |
|
2824 NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default"); |
|
2825 } |
|
2826 |
|
2827 if (appCSP) { |
|
2828 // Use the 1.0 CSP parser for apps if the pref to do so is set. |
|
2829 csp->AppendPolicy(appCSP, selfURI, false, specCompliantEnabled); |
|
2830 } |
|
2831 } |
|
2832 |
|
2833 // ----- if the doc is an app and specifies a CSP in its manifest, apply it. |
|
2834 if (applyAppManifestCSP) { |
|
2835 // Use the 1.0 CSP parser for apps if the pref to do so is set. |
|
2836 csp->AppendPolicy(appManifestCSP, selfURI, false, specCompliantEnabled); |
|
2837 } |
|
2838 |
|
2839 // While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers |
|
2840 // take priority. If both are present, the x-* headers are ignored. |
|
2841 |
|
2842 // ----- if there's a full-strength CSP header, apply it. |
|
2843 if (!cspHeaderValue.IsEmpty()) { |
|
2844 rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true); |
|
2845 NS_ENSURE_SUCCESS(rv, rv); |
|
2846 } else if (!cspOldHeaderValue.IsEmpty()) { |
|
2847 rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false); |
|
2848 NS_ENSURE_SUCCESS(rv, rv); |
|
2849 } |
|
2850 |
|
2851 // ----- if there's a report-only CSP header, apply it. |
|
2852 if (!cspROHeaderValue.IsEmpty()) { |
|
2853 rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true); |
|
2854 NS_ENSURE_SUCCESS(rv, rv); |
|
2855 } else if (!cspOldROHeaderValue.IsEmpty()) { |
|
2856 rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false); |
|
2857 NS_ENSURE_SUCCESS(rv, rv); |
|
2858 } |
|
2859 |
|
2860 // ----- Enforce frame-ancestor policy on any applied policies |
|
2861 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
2862 if (docShell) { |
|
2863 bool safeAncestry = false; |
|
2864 |
|
2865 // PermitsAncestry sends violation reports when necessary |
|
2866 rv = csp->PermitsAncestry(docShell, &safeAncestry); |
|
2867 NS_ENSURE_SUCCESS(rv, rv); |
|
2868 |
|
2869 if (!safeAncestry) { |
|
2870 #ifdef PR_LOGGING |
|
2871 PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
|
2872 ("CSP doesn't like frame's ancestry, not loading.")); |
|
2873 #endif |
|
2874 // stop! ERROR page! |
|
2875 aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); |
|
2876 } |
|
2877 } |
|
2878 |
|
2879 rv = principal->SetCsp(csp); |
|
2880 NS_ENSURE_SUCCESS(rv, rv); |
|
2881 #ifdef PR_LOGGING |
|
2882 PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
|
2883 ("Inserted CSP into principal %p", principal)); |
|
2884 #endif |
|
2885 |
|
2886 return NS_OK; |
|
2887 } |
|
2888 |
|
2889 void |
|
2890 nsDocument::StopDocumentLoad() |
|
2891 { |
|
2892 if (mParser) { |
|
2893 mParserAborted = true; |
|
2894 mParser->Terminate(); |
|
2895 } |
|
2896 } |
|
2897 |
|
2898 void |
|
2899 nsDocument::SetDocumentURI(nsIURI* aURI) |
|
2900 { |
|
2901 nsCOMPtr<nsIURI> oldBase = GetDocBaseURI(); |
|
2902 mDocumentURI = NS_TryToMakeImmutable(aURI); |
|
2903 nsIURI* newBase = GetDocBaseURI(); |
|
2904 |
|
2905 bool equalBases = false; |
|
2906 // Changing just the ref of a URI does not change how relative URIs would |
|
2907 // resolve wrt to it, so we can treat the bases as equal as long as they're |
|
2908 // equal ignoring the ref. |
|
2909 if (oldBase && newBase) { |
|
2910 oldBase->EqualsExceptRef(newBase, &equalBases); |
|
2911 } |
|
2912 else { |
|
2913 equalBases = !oldBase && !newBase; |
|
2914 } |
|
2915 |
|
2916 // If this is the first time we're setting the document's URI, set the |
|
2917 // document's original URI. |
|
2918 if (!mOriginalURI) |
|
2919 mOriginalURI = mDocumentURI; |
|
2920 |
|
2921 // If changing the document's URI changed the base URI of the document, we |
|
2922 // need to refresh the hrefs of all the links on the page. |
|
2923 if (!equalBases) { |
|
2924 RefreshLinkHrefs(); |
|
2925 } |
|
2926 } |
|
2927 |
|
2928 void |
|
2929 nsDocument::SetChromeXHRDocURI(nsIURI* aURI) |
|
2930 { |
|
2931 mChromeXHRDocURI = aURI; |
|
2932 } |
|
2933 |
|
2934 void |
|
2935 nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI) |
|
2936 { |
|
2937 mChromeXHRDocBaseURI = aURI; |
|
2938 } |
|
2939 |
|
2940 NS_IMETHODIMP |
|
2941 nsDocument::GetLastModified(nsAString& aLastModified) |
|
2942 { |
|
2943 nsIDocument::GetLastModified(aLastModified); |
|
2944 return NS_OK; |
|
2945 } |
|
2946 |
|
2947 void |
|
2948 nsIDocument::GetLastModified(nsAString& aLastModified) const |
|
2949 { |
|
2950 if (!mLastModified.IsEmpty()) { |
|
2951 aLastModified.Assign(mLastModified); |
|
2952 } else { |
|
2953 // If we for whatever reason failed to find the last modified time |
|
2954 // (or even the current time), fall back to what NS4.x returned. |
|
2955 aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00")); |
|
2956 } |
|
2957 } |
|
2958 |
|
2959 void |
|
2960 nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName) |
|
2961 { |
|
2962 MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement), |
|
2963 "Only put elements that need to be exposed as document['name'] in " |
|
2964 "the named table."); |
|
2965 |
|
2966 nsIdentifierMapEntry *entry = |
|
2967 mIdentifierMap.PutEntry(nsDependentAtomString(aName)); |
|
2968 |
|
2969 // Null for out-of-memory |
|
2970 if (entry) { |
|
2971 if (!entry->HasNameElement() && |
|
2972 !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
|
2973 ++mExpandoAndGeneration.generation; |
|
2974 } |
|
2975 entry->AddNameElement(this, aElement); |
|
2976 } |
|
2977 } |
|
2978 |
|
2979 void |
|
2980 nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName) |
|
2981 { |
|
2982 // Speed up document teardown |
|
2983 if (mIdentifierMap.Count() == 0) |
|
2984 return; |
|
2985 |
|
2986 nsIdentifierMapEntry *entry = |
|
2987 mIdentifierMap.GetEntry(nsDependentAtomString(aName)); |
|
2988 if (!entry) // Could be false if the element was anonymous, hence never added |
|
2989 return; |
|
2990 |
|
2991 entry->RemoveNameElement(aElement); |
|
2992 if (!entry->HasNameElement() && |
|
2993 !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
|
2994 ++mExpandoAndGeneration.generation; |
|
2995 } |
|
2996 } |
|
2997 |
|
2998 void |
|
2999 nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId) |
|
3000 { |
|
3001 nsIdentifierMapEntry *entry = |
|
3002 mIdentifierMap.PutEntry(nsDependentAtomString(aId)); |
|
3003 |
|
3004 if (entry) { /* True except on OOM */ |
|
3005 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && |
|
3006 !entry->HasNameElement() && |
|
3007 !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
|
3008 ++mExpandoAndGeneration.generation; |
|
3009 } |
|
3010 entry->AddIdElement(aElement); |
|
3011 } |
|
3012 } |
|
3013 |
|
3014 void |
|
3015 nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId) |
|
3016 { |
|
3017 NS_ASSERTION(aId, "huhwhatnow?"); |
|
3018 |
|
3019 // Speed up document teardown |
|
3020 if (mIdentifierMap.Count() == 0) { |
|
3021 return; |
|
3022 } |
|
3023 |
|
3024 nsIdentifierMapEntry *entry = |
|
3025 mIdentifierMap.GetEntry(nsDependentAtomString(aId)); |
|
3026 if (!entry) // Can be null for XML elements with changing ids. |
|
3027 return; |
|
3028 |
|
3029 entry->RemoveIdElement(aElement); |
|
3030 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && |
|
3031 !entry->HasNameElement() && |
|
3032 !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
|
3033 ++mExpandoAndGeneration.generation; |
|
3034 } |
|
3035 if (entry->IsEmpty()) { |
|
3036 mIdentifierMap.RawRemoveEntry(entry); |
|
3037 } |
|
3038 } |
|
3039 |
|
3040 nsIPrincipal* |
|
3041 nsDocument::GetPrincipal() |
|
3042 { |
|
3043 return NodePrincipal(); |
|
3044 } |
|
3045 |
|
3046 extern bool sDisablePrefetchHTTPSPref; |
|
3047 |
|
3048 void |
|
3049 nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal) |
|
3050 { |
|
3051 if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) { |
|
3052 nsCOMPtr<nsIURI> uri; |
|
3053 aNewPrincipal->GetURI(getter_AddRefs(uri)); |
|
3054 bool isHTTPS; |
|
3055 if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) || |
|
3056 isHTTPS) { |
|
3057 mAllowDNSPrefetch = false; |
|
3058 } |
|
3059 } |
|
3060 mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal); |
|
3061 } |
|
3062 |
|
3063 NS_IMETHODIMP |
|
3064 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache) |
|
3065 { |
|
3066 NS_IF_ADDREF(*aApplicationCache = mApplicationCache); |
|
3067 |
|
3068 return NS_OK; |
|
3069 } |
|
3070 |
|
3071 NS_IMETHODIMP |
|
3072 nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache) |
|
3073 { |
|
3074 mApplicationCache = aApplicationCache; |
|
3075 |
|
3076 return NS_OK; |
|
3077 } |
|
3078 |
|
3079 NS_IMETHODIMP |
|
3080 nsDocument::GetContentType(nsAString& aContentType) |
|
3081 { |
|
3082 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType); |
|
3083 |
|
3084 return NS_OK; |
|
3085 } |
|
3086 |
|
3087 void |
|
3088 nsDocument::SetContentType(const nsAString& aContentType) |
|
3089 { |
|
3090 NS_ASSERTION(GetContentTypeInternal().IsEmpty() || |
|
3091 GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)), |
|
3092 "Do you really want to change the content-type?"); |
|
3093 |
|
3094 SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType)); |
|
3095 } |
|
3096 |
|
3097 nsresult |
|
3098 nsDocument::GetAllowPlugins(bool * aAllowPlugins) |
|
3099 { |
|
3100 // First, we ask our docshell if it allows plugins. |
|
3101 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
3102 |
|
3103 if (docShell) { |
|
3104 docShell->GetAllowPlugins(aAllowPlugins); |
|
3105 |
|
3106 // If the docshell allows plugins, we check whether |
|
3107 // we are sandboxed and plugins should not be allowed. |
|
3108 if (*aAllowPlugins) |
|
3109 *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS); |
|
3110 } |
|
3111 |
|
3112 return NS_OK; |
|
3113 } |
|
3114 |
|
3115 already_AddRefed<UndoManager> |
|
3116 nsDocument::GetUndoManager() |
|
3117 { |
|
3118 Element* rootElement = GetRootElement(); |
|
3119 if (!rootElement) { |
|
3120 return nullptr; |
|
3121 } |
|
3122 |
|
3123 if (!mUndoManager) { |
|
3124 mUndoManager = new UndoManager(rootElement); |
|
3125 } |
|
3126 |
|
3127 nsRefPtr<UndoManager> undoManager = mUndoManager; |
|
3128 return undoManager.forget(); |
|
3129 } |
|
3130 |
|
3131 /* Return true if the document is in the focused top-level window, and is an |
|
3132 * ancestor of the focused DOMWindow. */ |
|
3133 NS_IMETHODIMP |
|
3134 nsDocument::HasFocus(bool* aResult) |
|
3135 { |
|
3136 ErrorResult rv; |
|
3137 *aResult = nsIDocument::HasFocus(rv); |
|
3138 return rv.ErrorCode(); |
|
3139 } |
|
3140 |
|
3141 bool |
|
3142 nsIDocument::HasFocus(ErrorResult& rv) const |
|
3143 { |
|
3144 nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
|
3145 if (!fm) { |
|
3146 rv.Throw(NS_ERROR_NOT_AVAILABLE); |
|
3147 return false; |
|
3148 } |
|
3149 |
|
3150 // Is there a focused DOMWindow? |
|
3151 nsCOMPtr<nsIDOMWindow> focusedWindow; |
|
3152 fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); |
|
3153 if (!focusedWindow) { |
|
3154 return false; |
|
3155 } |
|
3156 |
|
3157 // Are we an ancestor of the focused DOMWindow? |
|
3158 nsCOMPtr<nsIDOMDocument> domDocument; |
|
3159 focusedWindow->GetDocument(getter_AddRefs(domDocument)); |
|
3160 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument); |
|
3161 |
|
3162 for (nsIDocument* currentDoc = document; currentDoc; |
|
3163 currentDoc = currentDoc->GetParentDocument()) { |
|
3164 if (currentDoc == this) { |
|
3165 // Yes, we are an ancestor |
|
3166 return true; |
|
3167 } |
|
3168 } |
|
3169 |
|
3170 return false; |
|
3171 } |
|
3172 |
|
3173 NS_IMETHODIMP |
|
3174 nsDocument::GetReferrer(nsAString& aReferrer) |
|
3175 { |
|
3176 nsIDocument::GetReferrer(aReferrer); |
|
3177 return NS_OK; |
|
3178 } |
|
3179 |
|
3180 void |
|
3181 nsIDocument::GetReferrer(nsAString& aReferrer) const |
|
3182 { |
|
3183 if (mIsSrcdocDocument && mParentDocument) |
|
3184 mParentDocument->GetReferrer(aReferrer); |
|
3185 else |
|
3186 CopyUTF8toUTF16(mReferrer, aReferrer); |
|
3187 } |
|
3188 |
|
3189 nsresult |
|
3190 nsIDocument::GetSrcdocData(nsAString &aSrcdocData) |
|
3191 { |
|
3192 if (mIsSrcdocDocument) { |
|
3193 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel); |
|
3194 if (inStrmChan) { |
|
3195 return inStrmChan->GetSrcdocData(aSrcdocData); |
|
3196 } |
|
3197 } |
|
3198 aSrcdocData = NullString(); |
|
3199 return NS_OK; |
|
3200 } |
|
3201 |
|
3202 NS_IMETHODIMP |
|
3203 nsDocument::GetActiveElement(nsIDOMElement **aElement) |
|
3204 { |
|
3205 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement())); |
|
3206 el.forget(aElement); |
|
3207 return NS_OK; |
|
3208 } |
|
3209 |
|
3210 Element* |
|
3211 nsIDocument::GetActiveElement() |
|
3212 { |
|
3213 // Get the focused element. |
|
3214 nsCOMPtr<nsPIDOMWindow> window = GetWindow(); |
|
3215 if (window) { |
|
3216 nsCOMPtr<nsPIDOMWindow> focusedWindow; |
|
3217 nsIContent* focusedContent = |
|
3218 nsFocusManager::GetFocusedDescendant(window, false, |
|
3219 getter_AddRefs(focusedWindow)); |
|
3220 // be safe and make sure the element is from this document |
|
3221 if (focusedContent && focusedContent->OwnerDoc() == this) { |
|
3222 if (focusedContent->ChromeOnlyAccess()) { |
|
3223 focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); |
|
3224 } |
|
3225 if (focusedContent) { |
|
3226 return focusedContent->AsElement(); |
|
3227 } |
|
3228 } |
|
3229 } |
|
3230 |
|
3231 // No focused element anywhere in this document. Try to get the BODY. |
|
3232 nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument(); |
|
3233 if (htmlDoc) { |
|
3234 // Because of IE compatibility, return null when html document doesn't have |
|
3235 // a body. |
|
3236 return htmlDoc->GetBody(); |
|
3237 } |
|
3238 |
|
3239 // If we couldn't get a BODY, return the root element. |
|
3240 return GetDocumentElement(); |
|
3241 } |
|
3242 |
|
3243 NS_IMETHODIMP |
|
3244 nsDocument::GetCurrentScript(nsIDOMElement **aElement) |
|
3245 { |
|
3246 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript())); |
|
3247 el.forget(aElement); |
|
3248 return NS_OK; |
|
3249 } |
|
3250 |
|
3251 Element* |
|
3252 nsIDocument::GetCurrentScript() |
|
3253 { |
|
3254 nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript())); |
|
3255 return el; |
|
3256 } |
|
3257 |
|
3258 NS_IMETHODIMP |
|
3259 nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn) |
|
3260 { |
|
3261 Element* el = nsIDocument::ElementFromPoint(aX, aY); |
|
3262 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el); |
|
3263 retval.forget(aReturn); |
|
3264 return NS_OK; |
|
3265 } |
|
3266 |
|
3267 Element* |
|
3268 nsIDocument::ElementFromPoint(float aX, float aY) |
|
3269 { |
|
3270 return ElementFromPointHelper(aX, aY, false, true); |
|
3271 } |
|
3272 |
|
3273 Element* |
|
3274 nsDocument::ElementFromPointHelper(float aX, float aY, |
|
3275 bool aIgnoreRootScrollFrame, |
|
3276 bool aFlushLayout) |
|
3277 { |
|
3278 // As per the the spec, we return null if either coord is negative |
|
3279 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) { |
|
3280 return nullptr; |
|
3281 } |
|
3282 |
|
3283 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); |
|
3284 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); |
|
3285 nsPoint pt(x, y); |
|
3286 |
|
3287 // Make sure the layout information we get is up-to-date, and |
|
3288 // ensure we get a root frame (for everything but XUL) |
|
3289 if (aFlushLayout) |
|
3290 FlushPendingNotifications(Flush_Layout); |
|
3291 |
|
3292 nsIPresShell *ps = GetShell(); |
|
3293 if (!ps) { |
|
3294 return nullptr; |
|
3295 } |
|
3296 nsIFrame *rootFrame = ps->GetRootFrame(); |
|
3297 |
|
3298 // XUL docs, unlike HTML, have no frame tree until everything's done loading |
|
3299 if (!rootFrame) { |
|
3300 return nullptr; // return null to premature XUL callers as a reminder to wait |
|
3301 } |
|
3302 |
|
3303 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, |
|
3304 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | |
|
3305 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); |
|
3306 if (!ptFrame) { |
|
3307 return nullptr; |
|
3308 } |
|
3309 |
|
3310 nsIContent* elem = GetContentInThisDocument(ptFrame); |
|
3311 if (elem && !elem->IsElement()) { |
|
3312 elem = elem->GetParent(); |
|
3313 } |
|
3314 return elem ? elem->AsElement() : nullptr; |
|
3315 } |
|
3316 |
|
3317 nsresult |
|
3318 nsDocument::NodesFromRectHelper(float aX, float aY, |
|
3319 float aTopSize, float aRightSize, |
|
3320 float aBottomSize, float aLeftSize, |
|
3321 bool aIgnoreRootScrollFrame, |
|
3322 bool aFlushLayout, |
|
3323 nsIDOMNodeList** aReturn) |
|
3324 { |
|
3325 NS_ENSURE_ARG_POINTER(aReturn); |
|
3326 |
|
3327 nsSimpleContentList* elements = new nsSimpleContentList(this); |
|
3328 NS_ADDREF(elements); |
|
3329 *aReturn = elements; |
|
3330 |
|
3331 // Following the same behavior of elementFromPoint, |
|
3332 // we don't return anything if either coord is negative |
|
3333 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) |
|
3334 return NS_OK; |
|
3335 |
|
3336 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize); |
|
3337 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize); |
|
3338 nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1; |
|
3339 nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1; |
|
3340 |
|
3341 nsRect rect(x, y, w, h); |
|
3342 |
|
3343 // Make sure the layout information we get is up-to-date, and |
|
3344 // ensure we get a root frame (for everything but XUL) |
|
3345 if (aFlushLayout) { |
|
3346 FlushPendingNotifications(Flush_Layout); |
|
3347 } |
|
3348 |
|
3349 nsIPresShell *ps = GetShell(); |
|
3350 NS_ENSURE_STATE(ps); |
|
3351 nsIFrame *rootFrame = ps->GetRootFrame(); |
|
3352 |
|
3353 // XUL docs, unlike HTML, have no frame tree until everything's done loading |
|
3354 if (!rootFrame) |
|
3355 return NS_OK; // return nothing to premature XUL callers as a reminder to wait |
|
3356 |
|
3357 nsAutoTArray<nsIFrame*,8> outFrames; |
|
3358 nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames, |
|
3359 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | |
|
3360 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); |
|
3361 |
|
3362 // Used to filter out repeated elements in sequence. |
|
3363 nsIContent* lastAdded = nullptr; |
|
3364 |
|
3365 for (uint32_t i = 0; i < outFrames.Length(); i++) { |
|
3366 nsIContent* node = GetContentInThisDocument(outFrames[i]); |
|
3367 |
|
3368 if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) { |
|
3369 // We have a node that isn't an element or a text node, |
|
3370 // use its parent content instead. |
|
3371 node = node->GetParent(); |
|
3372 } |
|
3373 if (node && node != lastAdded) { |
|
3374 elements->AppendElement(node); |
|
3375 lastAdded = node; |
|
3376 } |
|
3377 } |
|
3378 |
|
3379 return NS_OK; |
|
3380 } |
|
3381 |
|
3382 NS_IMETHODIMP |
|
3383 nsDocument::GetElementsByClassName(const nsAString& aClasses, |
|
3384 nsIDOMNodeList** aReturn) |
|
3385 { |
|
3386 *aReturn = nsIDocument::GetElementsByClassName(aClasses).take(); |
|
3387 return NS_OK; |
|
3388 } |
|
3389 |
|
3390 already_AddRefed<nsContentList> |
|
3391 nsIDocument::GetElementsByClassName(const nsAString& aClasses) |
|
3392 { |
|
3393 return nsContentUtils::GetElementsByClassName(this, aClasses); |
|
3394 } |
|
3395 |
|
3396 NS_IMETHODIMP |
|
3397 nsDocument::ReleaseCapture() |
|
3398 { |
|
3399 nsIDocument::ReleaseCapture(); |
|
3400 return NS_OK; |
|
3401 } |
|
3402 |
|
3403 void |
|
3404 nsIDocument::ReleaseCapture() const |
|
3405 { |
|
3406 // only release the capture if the caller can access it. This prevents a |
|
3407 // page from stopping a scrollbar grab for example. |
|
3408 nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent(); |
|
3409 if (node && nsContentUtils::CanCallerAccess(node)) { |
|
3410 nsIPresShell::SetCapturingContent(nullptr, 0); |
|
3411 } |
|
3412 } |
|
3413 |
|
3414 already_AddRefed<nsIURI> |
|
3415 nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const |
|
3416 { |
|
3417 nsCOMPtr<nsIURI> uri; |
|
3418 if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) { |
|
3419 uri = mChromeXHRDocBaseURI; |
|
3420 } else { |
|
3421 uri = GetDocBaseURI(); |
|
3422 } |
|
3423 |
|
3424 return uri.forget(); |
|
3425 } |
|
3426 |
|
3427 nsresult |
|
3428 nsDocument::SetBaseURI(nsIURI* aURI) |
|
3429 { |
|
3430 if (!aURI && !mDocumentBaseURI) { |
|
3431 return NS_OK; |
|
3432 } |
|
3433 |
|
3434 // Don't do anything if the URI wasn't actually changed. |
|
3435 if (aURI && mDocumentBaseURI) { |
|
3436 bool equalBases = false; |
|
3437 mDocumentBaseURI->Equals(aURI, &equalBases); |
|
3438 if (equalBases) { |
|
3439 return NS_OK; |
|
3440 } |
|
3441 } |
|
3442 |
|
3443 if (aURI) { |
|
3444 mDocumentBaseURI = NS_TryToMakeImmutable(aURI); |
|
3445 } else { |
|
3446 mDocumentBaseURI = nullptr; |
|
3447 } |
|
3448 RefreshLinkHrefs(); |
|
3449 |
|
3450 return NS_OK; |
|
3451 } |
|
3452 |
|
3453 void |
|
3454 nsDocument::GetBaseTarget(nsAString &aBaseTarget) |
|
3455 { |
|
3456 aBaseTarget = mBaseTarget; |
|
3457 } |
|
3458 |
|
3459 void |
|
3460 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID) |
|
3461 { |
|
3462 // XXX it would be a good idea to assert the sanity of the argument, |
|
3463 // but before we figure out what to do about non-Encoding Standard |
|
3464 // encodings in the charset menu and in mailnews, assertions are futile. |
|
3465 if (!mCharacterSet.Equals(aCharSetID)) { |
|
3466 mCharacterSet = aCharSetID; |
|
3467 |
|
3468 int32_t n = mCharSetObservers.Length(); |
|
3469 |
|
3470 for (int32_t i = 0; i < n; i++) { |
|
3471 nsIObserver* observer = mCharSetObservers.ElementAt(i); |
|
3472 |
|
3473 observer->Observe(static_cast<nsIDocument *>(this), "charset", |
|
3474 NS_ConvertASCIItoUTF16(aCharSetID).get()); |
|
3475 } |
|
3476 } |
|
3477 } |
|
3478 |
|
3479 nsresult |
|
3480 nsDocument::AddCharSetObserver(nsIObserver* aObserver) |
|
3481 { |
|
3482 NS_ENSURE_ARG_POINTER(aObserver); |
|
3483 |
|
3484 NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE); |
|
3485 |
|
3486 return NS_OK; |
|
3487 } |
|
3488 |
|
3489 void |
|
3490 nsDocument::RemoveCharSetObserver(nsIObserver* aObserver) |
|
3491 { |
|
3492 mCharSetObservers.RemoveElement(aObserver); |
|
3493 } |
|
3494 |
|
3495 void |
|
3496 nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const |
|
3497 { |
|
3498 aData.Truncate(); |
|
3499 const nsDocHeaderData* data = mHeaderData; |
|
3500 while (data) { |
|
3501 if (data->mField == aHeaderField) { |
|
3502 aData = data->mData; |
|
3503 |
|
3504 break; |
|
3505 } |
|
3506 data = data->mNext; |
|
3507 } |
|
3508 } |
|
3509 |
|
3510 void |
|
3511 nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData) |
|
3512 { |
|
3513 if (!aHeaderField) { |
|
3514 NS_ERROR("null headerField"); |
|
3515 return; |
|
3516 } |
|
3517 |
|
3518 if (!mHeaderData) { |
|
3519 if (!aData.IsEmpty()) { // don't bother storing empty string |
|
3520 mHeaderData = new nsDocHeaderData(aHeaderField, aData); |
|
3521 } |
|
3522 } |
|
3523 else { |
|
3524 nsDocHeaderData* data = mHeaderData; |
|
3525 nsDocHeaderData** lastPtr = &mHeaderData; |
|
3526 bool found = false; |
|
3527 do { // look for existing and replace |
|
3528 if (data->mField == aHeaderField) { |
|
3529 if (!aData.IsEmpty()) { |
|
3530 data->mData.Assign(aData); |
|
3531 } |
|
3532 else { // don't store empty string |
|
3533 *lastPtr = data->mNext; |
|
3534 data->mNext = nullptr; |
|
3535 delete data; |
|
3536 } |
|
3537 found = true; |
|
3538 |
|
3539 break; |
|
3540 } |
|
3541 lastPtr = &(data->mNext); |
|
3542 data = *lastPtr; |
|
3543 } while (data); |
|
3544 |
|
3545 if (!aData.IsEmpty() && !found) { |
|
3546 // didn't find, append |
|
3547 *lastPtr = new nsDocHeaderData(aHeaderField, aData); |
|
3548 } |
|
3549 } |
|
3550 |
|
3551 if (aHeaderField == nsGkAtoms::headerContentLanguage) { |
|
3552 CopyUTF16toUTF8(aData, mContentLanguage); |
|
3553 } |
|
3554 |
|
3555 if (aHeaderField == nsGkAtoms::headerDefaultStyle) { |
|
3556 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per |
|
3557 // spec. |
|
3558 if (DOMStringIsNull(mLastStyleSheetSet)) { |
|
3559 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet, |
|
3560 // per spec. The idea here is that we're changing our preferred set and |
|
3561 // that shouldn't change the value of lastStyleSheetSet. Also, we're |
|
3562 // using the Internal version so we can update the CSSLoader and not have |
|
3563 // to worry about null strings. |
|
3564 EnableStyleSheetsForSetInternal(aData, true); |
|
3565 } |
|
3566 } |
|
3567 |
|
3568 if (aHeaderField == nsGkAtoms::refresh) { |
|
3569 // We get into this code before we have a script global yet, so get to |
|
3570 // our container via mDocumentContainer. |
|
3571 nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer); |
|
3572 if (refresher) { |
|
3573 // Note: using mDocumentURI instead of mBaseURI here, for consistency |
|
3574 // (used to just use the current URI of our webnavigation, but that |
|
3575 // should really be the same thing). Note that this code can run |
|
3576 // before the current URI of the webnavigation has been updated, so we |
|
3577 // can't assert equality here. |
|
3578 refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(), |
|
3579 NS_ConvertUTF16toUTF8(aData)); |
|
3580 } |
|
3581 } |
|
3582 |
|
3583 if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl && |
|
3584 mAllowDNSPrefetch) { |
|
3585 // Chromium treats any value other than 'on' (case insensitive) as 'off'. |
|
3586 mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on"); |
|
3587 } |
|
3588 |
|
3589 if (aHeaderField == nsGkAtoms::viewport || |
|
3590 aHeaderField == nsGkAtoms::handheldFriendly || |
|
3591 aHeaderField == nsGkAtoms::viewport_minimum_scale || |
|
3592 aHeaderField == nsGkAtoms::viewport_maximum_scale || |
|
3593 aHeaderField == nsGkAtoms::viewport_initial_scale || |
|
3594 aHeaderField == nsGkAtoms::viewport_height || |
|
3595 aHeaderField == nsGkAtoms::viewport_width || |
|
3596 aHeaderField == nsGkAtoms::viewport_user_scalable) { |
|
3597 mViewportType = Unknown; |
|
3598 } |
|
3599 } |
|
3600 |
|
3601 void |
|
3602 nsDocument::TryChannelCharset(nsIChannel *aChannel, |
|
3603 int32_t& aCharsetSource, |
|
3604 nsACString& aCharset, |
|
3605 nsHtml5TreeOpExecutor* aExecutor) |
|
3606 { |
|
3607 if (aChannel) { |
|
3608 nsAutoCString charsetVal; |
|
3609 nsresult rv = aChannel->GetContentCharset(charsetVal); |
|
3610 if (NS_SUCCEEDED(rv)) { |
|
3611 nsAutoCString preferred; |
|
3612 if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) { |
|
3613 aCharset = preferred; |
|
3614 aCharsetSource = kCharsetFromChannel; |
|
3615 return; |
|
3616 } else if (aExecutor && !charsetVal.IsEmpty()) { |
|
3617 aExecutor->ComplainAboutBogusProtocolCharset(this); |
|
3618 } |
|
3619 } |
|
3620 } |
|
3621 } |
|
3622 |
|
3623 already_AddRefed<nsIPresShell> |
|
3624 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager, |
|
3625 nsStyleSet* aStyleSet) |
|
3626 { |
|
3627 // Don't add anything here. Add it to |doCreateShell| instead. |
|
3628 // This exists so that subclasses can pass other values for the 4th |
|
3629 // parameter some of the time. |
|
3630 return doCreateShell(aContext, aViewManager, aStyleSet, |
|
3631 eCompatibility_FullStandards); |
|
3632 } |
|
3633 |
|
3634 already_AddRefed<nsIPresShell> |
|
3635 nsDocument::doCreateShell(nsPresContext* aContext, |
|
3636 nsViewManager* aViewManager, nsStyleSet* aStyleSet, |
|
3637 nsCompatibility aCompatMode) |
|
3638 { |
|
3639 NS_ASSERTION(!mPresShell, "We have a presshell already!"); |
|
3640 |
|
3641 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr); |
|
3642 |
|
3643 FillStyleSet(aStyleSet); |
|
3644 |
|
3645 nsRefPtr<PresShell> shell = new PresShell; |
|
3646 shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode); |
|
3647 |
|
3648 // Note: we don't hold a ref to the shell (it holds a ref to us) |
|
3649 mPresShell = shell; |
|
3650 |
|
3651 // Make sure to never paint if we belong to an invisible DocShell. |
|
3652 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
3653 if (docShell && docShell->IsInvisible()) |
|
3654 shell->SetNeverPainting(true); |
|
3655 |
|
3656 mExternalResourceMap.ShowViewers(); |
|
3657 |
|
3658 MaybeRescheduleAnimationFrameNotifications(); |
|
3659 |
|
3660 return shell.forget(); |
|
3661 } |
|
3662 |
|
3663 void |
|
3664 nsDocument::MaybeRescheduleAnimationFrameNotifications() |
|
3665 { |
|
3666 if (!mPresShell || !IsEventHandlingEnabled()) { |
|
3667 // bail out for now, until one of those conditions changes |
|
3668 return; |
|
3669 } |
|
3670 |
|
3671 nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver(); |
|
3672 if (!mFrameRequestCallbacks.IsEmpty()) { |
|
3673 rd->ScheduleFrameRequestCallbacks(this); |
|
3674 } |
|
3675 } |
|
3676 |
|
3677 void |
|
3678 nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks) |
|
3679 { |
|
3680 aCallbacks.AppendElements(mFrameRequestCallbacks); |
|
3681 mFrameRequestCallbacks.Clear(); |
|
3682 } |
|
3683 |
|
3684 PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey, |
|
3685 uint32_t aData, |
|
3686 void* userArg) |
|
3687 { |
|
3688 aKey->RequestDiscard(); |
|
3689 return PL_DHASH_NEXT; |
|
3690 } |
|
3691 |
|
3692 void |
|
3693 nsDocument::DeleteShell() |
|
3694 { |
|
3695 mExternalResourceMap.HideViewers(); |
|
3696 if (IsEventHandlingEnabled()) { |
|
3697 RevokeAnimationFrameNotifications(); |
|
3698 } |
|
3699 |
|
3700 // When our shell goes away, request that all our images be immediately |
|
3701 // discarded, so we don't carry around decoded image data for a document we |
|
3702 // no longer intend to paint. |
|
3703 mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr); |
|
3704 |
|
3705 mPresShell = nullptr; |
|
3706 } |
|
3707 |
|
3708 void |
|
3709 nsDocument::RevokeAnimationFrameNotifications() |
|
3710 { |
|
3711 if (!mFrameRequestCallbacks.IsEmpty()) { |
|
3712 mPresShell->GetPresContext()->RefreshDriver()-> |
|
3713 RevokeFrameRequestCallbacks(this); |
|
3714 } |
|
3715 } |
|
3716 |
|
3717 static void |
|
3718 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) |
|
3719 { |
|
3720 SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry); |
|
3721 |
|
3722 NS_RELEASE(e->mKey); |
|
3723 if (e->mSubDocument) { |
|
3724 e->mSubDocument->SetParentDocument(nullptr); |
|
3725 NS_RELEASE(e->mSubDocument); |
|
3726 } |
|
3727 } |
|
3728 |
|
3729 static bool |
|
3730 SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key) |
|
3731 { |
|
3732 SubDocMapEntry *e = |
|
3733 const_cast<SubDocMapEntry *> |
|
3734 (static_cast<const SubDocMapEntry *>(entry)); |
|
3735 |
|
3736 e->mKey = const_cast<Element*>(static_cast<const Element*>(key)); |
|
3737 NS_ADDREF(e->mKey); |
|
3738 |
|
3739 e->mSubDocument = nullptr; |
|
3740 return true; |
|
3741 } |
|
3742 |
|
3743 nsresult |
|
3744 nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc) |
|
3745 { |
|
3746 NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED); |
|
3747 |
|
3748 if (!aSubDoc) { |
|
3749 // aSubDoc is nullptr, remove the mapping |
|
3750 |
|
3751 if (mSubDocuments) { |
|
3752 SubDocMapEntry *entry = |
|
3753 static_cast<SubDocMapEntry*> |
|
3754 (PL_DHashTableOperate(mSubDocuments, aElement, |
|
3755 PL_DHASH_LOOKUP)); |
|
3756 |
|
3757 if (PL_DHASH_ENTRY_IS_BUSY(entry)) { |
|
3758 PL_DHashTableRawRemove(mSubDocuments, entry); |
|
3759 } |
|
3760 } |
|
3761 } else { |
|
3762 if (!mSubDocuments) { |
|
3763 // Create a new hashtable |
|
3764 |
|
3765 static const PLDHashTableOps hash_table_ops = |
|
3766 { |
|
3767 PL_DHashAllocTable, |
|
3768 PL_DHashFreeTable, |
|
3769 PL_DHashVoidPtrKeyStub, |
|
3770 PL_DHashMatchEntryStub, |
|
3771 PL_DHashMoveEntryStub, |
|
3772 SubDocClearEntry, |
|
3773 PL_DHashFinalizeStub, |
|
3774 SubDocInitEntry |
|
3775 }; |
|
3776 |
|
3777 mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr, |
|
3778 sizeof(SubDocMapEntry), 16); |
|
3779 if (!mSubDocuments) { |
|
3780 return NS_ERROR_OUT_OF_MEMORY; |
|
3781 } |
|
3782 } |
|
3783 |
|
3784 // Add a mapping to the hash table |
|
3785 SubDocMapEntry *entry = |
|
3786 static_cast<SubDocMapEntry*> |
|
3787 (PL_DHashTableOperate(mSubDocuments, aElement, |
|
3788 PL_DHASH_ADD)); |
|
3789 |
|
3790 if (!entry) { |
|
3791 return NS_ERROR_OUT_OF_MEMORY; |
|
3792 } |
|
3793 |
|
3794 if (entry->mSubDocument) { |
|
3795 entry->mSubDocument->SetParentDocument(nullptr); |
|
3796 |
|
3797 // Release the old sub document |
|
3798 NS_RELEASE(entry->mSubDocument); |
|
3799 } |
|
3800 |
|
3801 entry->mSubDocument = aSubDoc; |
|
3802 NS_ADDREF(entry->mSubDocument); |
|
3803 |
|
3804 aSubDoc->SetParentDocument(this); |
|
3805 } |
|
3806 |
|
3807 return NS_OK; |
|
3808 } |
|
3809 |
|
3810 nsIDocument* |
|
3811 nsDocument::GetSubDocumentFor(nsIContent *aContent) const |
|
3812 { |
|
3813 if (mSubDocuments && aContent->IsElement()) { |
|
3814 SubDocMapEntry *entry = |
|
3815 static_cast<SubDocMapEntry*> |
|
3816 (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(), |
|
3817 PL_DHASH_LOOKUP)); |
|
3818 |
|
3819 if (PL_DHASH_ENTRY_IS_BUSY(entry)) { |
|
3820 return entry->mSubDocument; |
|
3821 } |
|
3822 } |
|
3823 |
|
3824 return nullptr; |
|
3825 } |
|
3826 |
|
3827 static PLDHashOperator |
|
3828 FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, |
|
3829 uint32_t number, void *arg) |
|
3830 { |
|
3831 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
|
3832 FindContentData *data = static_cast<FindContentData*>(arg); |
|
3833 |
|
3834 if (entry->mSubDocument == data->mSubDocument) { |
|
3835 data->mResult = entry->mKey; |
|
3836 |
|
3837 return PL_DHASH_STOP; |
|
3838 } |
|
3839 |
|
3840 return PL_DHASH_NEXT; |
|
3841 } |
|
3842 |
|
3843 Element* |
|
3844 nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const |
|
3845 { |
|
3846 NS_ENSURE_TRUE(aDocument, nullptr); |
|
3847 |
|
3848 if (!mSubDocuments) { |
|
3849 return nullptr; |
|
3850 } |
|
3851 |
|
3852 FindContentData data(aDocument); |
|
3853 PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data); |
|
3854 |
|
3855 return data.mResult; |
|
3856 } |
|
3857 |
|
3858 bool |
|
3859 nsDocument::IsNodeOfType(uint32_t aFlags) const |
|
3860 { |
|
3861 return !(aFlags & ~eDOCUMENT); |
|
3862 } |
|
3863 |
|
3864 Element* |
|
3865 nsIDocument::GetRootElement() const |
|
3866 { |
|
3867 return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ? |
|
3868 mCachedRootElement : GetRootElementInternal(); |
|
3869 } |
|
3870 |
|
3871 Element* |
|
3872 nsDocument::GetRootElementInternal() const |
|
3873 { |
|
3874 // Loop backwards because any non-elements, such as doctypes and PIs |
|
3875 // are likely to appear before the root element. |
|
3876 uint32_t i; |
|
3877 for (i = mChildren.ChildCount(); i > 0; --i) { |
|
3878 nsIContent* child = mChildren.ChildAt(i - 1); |
|
3879 if (child->IsElement()) { |
|
3880 const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement(); |
|
3881 return child->AsElement(); |
|
3882 } |
|
3883 } |
|
3884 |
|
3885 const_cast<nsDocument*>(this)->mCachedRootElement = nullptr; |
|
3886 return nullptr; |
|
3887 } |
|
3888 |
|
3889 nsIContent * |
|
3890 nsDocument::GetChildAt(uint32_t aIndex) const |
|
3891 { |
|
3892 return mChildren.GetSafeChildAt(aIndex); |
|
3893 } |
|
3894 |
|
3895 int32_t |
|
3896 nsDocument::IndexOf(const nsINode* aPossibleChild) const |
|
3897 { |
|
3898 return mChildren.IndexOfChild(aPossibleChild); |
|
3899 } |
|
3900 |
|
3901 uint32_t |
|
3902 nsDocument::GetChildCount() const |
|
3903 { |
|
3904 return mChildren.ChildCount(); |
|
3905 } |
|
3906 |
|
3907 nsIContent * const * |
|
3908 nsDocument::GetChildArray(uint32_t* aChildCount) const |
|
3909 { |
|
3910 return mChildren.GetChildArray(aChildCount); |
|
3911 } |
|
3912 |
|
3913 |
|
3914 nsresult |
|
3915 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex, |
|
3916 bool aNotify) |
|
3917 { |
|
3918 if (aKid->IsElement() && GetRootElement()) { |
|
3919 NS_WARNING("Inserting root element when we already have one"); |
|
3920 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
3921 } |
|
3922 |
|
3923 return doInsertChildAt(aKid, aIndex, aNotify, mChildren); |
|
3924 } |
|
3925 |
|
3926 nsresult |
|
3927 nsDocument::AppendChildTo(nsIContent* aKid, bool aNotify) |
|
3928 { |
|
3929 // Make sure to _not_ call the subclass InsertChildAt here. If |
|
3930 // subclasses wanted to hook into this stuff, they would have |
|
3931 // overridden AppendChildTo. |
|
3932 // XXXbz maybe this should just be a non-virtual method on nsINode? |
|
3933 // Feels that way to me... |
|
3934 return nsDocument::InsertChildAt(aKid, GetChildCount(), aNotify); |
|
3935 } |
|
3936 |
|
3937 void |
|
3938 nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify) |
|
3939 { |
|
3940 nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex); |
|
3941 if (!oldKid) { |
|
3942 return; |
|
3943 } |
|
3944 |
|
3945 if (oldKid->IsElement()) { |
|
3946 // Destroy the link map up front before we mess with the child list. |
|
3947 DestroyElementMaps(); |
|
3948 } |
|
3949 |
|
3950 doRemoveChildAt(aIndex, aNotify, oldKid, mChildren); |
|
3951 mCachedRootElement = nullptr; |
|
3952 } |
|
3953 |
|
3954 int32_t |
|
3955 nsDocument::GetNumberOfStyleSheets() const |
|
3956 { |
|
3957 return mStyleSheets.Count(); |
|
3958 } |
|
3959 |
|
3960 nsIStyleSheet* |
|
3961 nsDocument::GetStyleSheetAt(int32_t aIndex) const |
|
3962 { |
|
3963 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr); |
|
3964 return mStyleSheets[aIndex]; |
|
3965 } |
|
3966 |
|
3967 int32_t |
|
3968 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const |
|
3969 { |
|
3970 return mStyleSheets.IndexOf(aSheet); |
|
3971 } |
|
3972 |
|
3973 void |
|
3974 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet) |
|
3975 { |
|
3976 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
3977 if (shell) { |
|
3978 shell->StyleSet()->AddDocStyleSheet(aSheet, this); |
|
3979 } |
|
3980 } |
|
3981 |
|
3982 #define DO_STYLESHEET_NOTIFICATION(createFunc, concreteInterface, initMethod, type, ...) \ |
|
3983 do { \ |
|
3984 nsCOMPtr<nsIDOMEvent> event; \ |
|
3985 nsresult rv = createFunc(getter_AddRefs(event), this, \ |
|
3986 mPresShell ? \ |
|
3987 mPresShell->GetPresContext() : nullptr, \ |
|
3988 nullptr); \ |
|
3989 if (NS_FAILED(rv)) { \ |
|
3990 return; \ |
|
3991 } \ |
|
3992 nsCOMPtr<nsIDOMCSSStyleSheet> cssSheet(do_QueryInterface(aSheet)); \ |
|
3993 if (!cssSheet) { \ |
|
3994 return; \ |
|
3995 } \ |
|
3996 nsCOMPtr<concreteInterface> ssEvent(do_QueryInterface(event)); \ |
|
3997 MOZ_ASSERT(ssEvent); \ |
|
3998 ssEvent->initMethod(NS_LITERAL_STRING(type), true, true, \ |
|
3999 cssSheet, __VA_ARGS__); \ |
|
4000 event->SetTrusted(true); \ |
|
4001 event->SetTarget(this); \ |
|
4002 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = \ |
|
4003 new AsyncEventDispatcher(this, event); \ |
|
4004 asyncDispatcher->mDispatchChromeOnly = true; \ |
|
4005 asyncDispatcher->PostDOMEvent(); \ |
|
4006 } while (0); |
|
4007 |
|
4008 void |
|
4009 nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet) |
|
4010 { |
|
4011 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet)); |
|
4012 |
|
4013 if (StyleSheetChangeEventsEnabled()) { |
|
4014 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent, |
|
4015 nsIDOMStyleSheetChangeEvent, |
|
4016 InitStyleSheetChangeEvent, |
|
4017 "StyleSheetAdded", |
|
4018 aDocumentSheet); |
|
4019 } |
|
4020 } |
|
4021 |
|
4022 void |
|
4023 nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet) |
|
4024 { |
|
4025 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet)); |
|
4026 |
|
4027 if (StyleSheetChangeEventsEnabled()) { |
|
4028 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent, |
|
4029 nsIDOMStyleSheetChangeEvent, |
|
4030 InitStyleSheetChangeEvent, |
|
4031 "StyleSheetRemoved", |
|
4032 aDocumentSheet); |
|
4033 } |
|
4034 } |
|
4035 |
|
4036 void |
|
4037 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet) |
|
4038 { |
|
4039 NS_PRECONDITION(aSheet, "null arg"); |
|
4040 mStyleSheets.AppendObject(aSheet); |
|
4041 aSheet->SetOwningDocument(this); |
|
4042 |
|
4043 if (aSheet->IsApplicable()) { |
|
4044 AddStyleSheetToStyleSets(aSheet); |
|
4045 } |
|
4046 |
|
4047 NotifyStyleSheetAdded(aSheet, true); |
|
4048 } |
|
4049 |
|
4050 void |
|
4051 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet) |
|
4052 { |
|
4053 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
4054 if (shell) { |
|
4055 shell->StyleSet()->RemoveDocStyleSheet(aSheet); |
|
4056 } |
|
4057 } |
|
4058 |
|
4059 void |
|
4060 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet) |
|
4061 { |
|
4062 NS_PRECONDITION(aSheet, "null arg"); |
|
4063 nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon |
|
4064 |
|
4065 if (!mStyleSheets.RemoveObject(aSheet)) { |
|
4066 NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found"); |
|
4067 return; |
|
4068 } |
|
4069 |
|
4070 if (!mIsGoingAway) { |
|
4071 if (aSheet->IsApplicable()) { |
|
4072 RemoveStyleSheetFromStyleSets(aSheet); |
|
4073 } |
|
4074 |
|
4075 NotifyStyleSheetRemoved(aSheet, true); |
|
4076 } |
|
4077 |
|
4078 aSheet->SetOwningDocument(nullptr); |
|
4079 } |
|
4080 |
|
4081 void |
|
4082 nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets, |
|
4083 nsCOMArray<nsIStyleSheet>& aNewSheets) |
|
4084 { |
|
4085 BeginUpdate(UPDATE_STYLE); |
|
4086 |
|
4087 // XXX Need to set the sheet on the ownernode, if any |
|
4088 NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(), |
|
4089 "The lists must be the same length!"); |
|
4090 int32_t count = aOldSheets.Count(); |
|
4091 |
|
4092 nsCOMPtr<nsIStyleSheet> oldSheet; |
|
4093 int32_t i; |
|
4094 for (i = 0; i < count; ++i) { |
|
4095 oldSheet = aOldSheets[i]; |
|
4096 |
|
4097 // First remove the old sheet. |
|
4098 NS_ASSERTION(oldSheet, "None of the old sheets should be null"); |
|
4099 int32_t oldIndex = mStyleSheets.IndexOf(oldSheet); |
|
4100 RemoveStyleSheet(oldSheet); // This does the right notifications |
|
4101 |
|
4102 // Now put the new one in its place. If it's null, just ignore it. |
|
4103 nsIStyleSheet* newSheet = aNewSheets[i]; |
|
4104 if (newSheet) { |
|
4105 mStyleSheets.InsertObjectAt(newSheet, oldIndex); |
|
4106 newSheet->SetOwningDocument(this); |
|
4107 if (newSheet->IsApplicable()) { |
|
4108 AddStyleSheetToStyleSets(newSheet); |
|
4109 } |
|
4110 |
|
4111 NotifyStyleSheetAdded(newSheet, true); |
|
4112 } |
|
4113 } |
|
4114 |
|
4115 EndUpdate(UPDATE_STYLE); |
|
4116 } |
|
4117 |
|
4118 void |
|
4119 nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex) |
|
4120 { |
|
4121 NS_PRECONDITION(aSheet, "null ptr"); |
|
4122 mStyleSheets.InsertObjectAt(aSheet, aIndex); |
|
4123 |
|
4124 aSheet->SetOwningDocument(this); |
|
4125 |
|
4126 if (aSheet->IsApplicable()) { |
|
4127 AddStyleSheetToStyleSets(aSheet); |
|
4128 } |
|
4129 |
|
4130 NotifyStyleSheetAdded(aSheet, true); |
|
4131 } |
|
4132 |
|
4133 |
|
4134 void |
|
4135 nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet, |
|
4136 bool aApplicable) |
|
4137 { |
|
4138 NS_PRECONDITION(aSheet, "null arg"); |
|
4139 |
|
4140 // If we're actually in the document style sheet list |
|
4141 if (-1 != mStyleSheets.IndexOf(aSheet)) { |
|
4142 if (aApplicable) { |
|
4143 AddStyleSheetToStyleSets(aSheet); |
|
4144 } else { |
|
4145 RemoveStyleSheetFromStyleSets(aSheet); |
|
4146 } |
|
4147 } |
|
4148 |
|
4149 // We have to always notify, since this will be called for sheets |
|
4150 // that are children of sheets in our style set, as well as some |
|
4151 // sheets for nsHTMLEditor. |
|
4152 |
|
4153 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, |
|
4154 (this, aSheet, aApplicable)); |
|
4155 |
|
4156 if (StyleSheetChangeEventsEnabled()) { |
|
4157 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetApplicableStateChangeEvent, |
|
4158 nsIDOMStyleSheetApplicableStateChangeEvent, |
|
4159 InitStyleSheetApplicableStateChangeEvent, |
|
4160 "StyleSheetApplicableStateChanged", |
|
4161 aApplicable); |
|
4162 } |
|
4163 |
|
4164 if (!mSSApplicableStateNotificationPending) { |
|
4165 nsRefPtr<nsIRunnable> notification = NS_NewRunnableMethod(this, |
|
4166 &nsDocument::NotifyStyleSheetApplicableStateChanged); |
|
4167 mSSApplicableStateNotificationPending = |
|
4168 NS_SUCCEEDED(NS_DispatchToCurrentThread(notification)); |
|
4169 } |
|
4170 } |
|
4171 |
|
4172 void |
|
4173 nsDocument::NotifyStyleSheetApplicableStateChanged() |
|
4174 { |
|
4175 mSSApplicableStateNotificationPending = false; |
|
4176 nsCOMPtr<nsIObserverService> observerService = |
|
4177 mozilla::services::GetObserverService(); |
|
4178 if (observerService) { |
|
4179 observerService->NotifyObservers(static_cast<nsIDocument*>(this), |
|
4180 "style-sheet-applicable-state-changed", |
|
4181 nullptr); |
|
4182 } |
|
4183 } |
|
4184 |
|
4185 // These three functions are a lot like the implementation of the |
|
4186 // corresponding API for regular stylesheets. |
|
4187 |
|
4188 int32_t |
|
4189 nsDocument::GetNumberOfCatalogStyleSheets() const |
|
4190 { |
|
4191 return mCatalogSheets.Count(); |
|
4192 } |
|
4193 |
|
4194 nsIStyleSheet* |
|
4195 nsDocument::GetCatalogStyleSheetAt(int32_t aIndex) const |
|
4196 { |
|
4197 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mCatalogSheets.Count(), nullptr); |
|
4198 return mCatalogSheets[aIndex]; |
|
4199 } |
|
4200 |
|
4201 void |
|
4202 nsDocument::AddCatalogStyleSheet(nsCSSStyleSheet* aSheet) |
|
4203 { |
|
4204 mCatalogSheets.AppendObject(aSheet); |
|
4205 aSheet->SetOwningDocument(this); |
|
4206 aSheet->SetOwningNode(this); |
|
4207 |
|
4208 if (aSheet->IsApplicable()) { |
|
4209 // This is like |AddStyleSheetToStyleSets|, but for an agent sheet. |
|
4210 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
4211 if (shell) { |
|
4212 shell->StyleSet()->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet); |
|
4213 } |
|
4214 } |
|
4215 |
|
4216 NotifyStyleSheetAdded(aSheet, false); |
|
4217 } |
|
4218 |
|
4219 void |
|
4220 nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI) |
|
4221 { |
|
4222 mozilla::css::Loader* cssLoader = CSSLoader(); |
|
4223 if (cssLoader->GetEnabled()) { |
|
4224 int32_t sheetCount = GetNumberOfCatalogStyleSheets(); |
|
4225 for (int32_t i = 0; i < sheetCount; i++) { |
|
4226 nsIStyleSheet* sheet = GetCatalogStyleSheetAt(i); |
|
4227 NS_ASSERTION(sheet, "unexpected null stylesheet in the document"); |
|
4228 if (sheet) { |
|
4229 nsAutoCString uriStr; |
|
4230 sheet->GetSheetURI()->GetSpec(uriStr); |
|
4231 if (uriStr.Equals(aStyleSheetURI)) |
|
4232 return; |
|
4233 } |
|
4234 } |
|
4235 |
|
4236 nsCOMPtr<nsIURI> uri; |
|
4237 NS_NewURI(getter_AddRefs(uri), aStyleSheetURI); |
|
4238 if (uri) { |
|
4239 nsRefPtr<nsCSSStyleSheet> sheet; |
|
4240 cssLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet)); |
|
4241 if (sheet) { |
|
4242 BeginUpdate(UPDATE_STYLE); |
|
4243 AddCatalogStyleSheet(sheet); |
|
4244 EndUpdate(UPDATE_STYLE); |
|
4245 } |
|
4246 } |
|
4247 } |
|
4248 } |
|
4249 |
|
4250 static nsStyleSet::sheetType |
|
4251 ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType) |
|
4252 { |
|
4253 switch(aType) { |
|
4254 case nsIDocument::eAgentSheet: |
|
4255 return nsStyleSet::eAgentSheet; |
|
4256 case nsIDocument::eUserSheet: |
|
4257 return nsStyleSet::eUserSheet; |
|
4258 case nsIDocument::eAuthorSheet: |
|
4259 return nsStyleSet::eDocSheet; |
|
4260 default: |
|
4261 NS_ASSERTION(false, "wrong type"); |
|
4262 // we must return something although this should never happen |
|
4263 return nsStyleSet::eSheetTypeCount; |
|
4264 } |
|
4265 } |
|
4266 |
|
4267 static int32_t |
|
4268 FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI) |
|
4269 { |
|
4270 for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) { |
|
4271 bool bEqual; |
|
4272 nsIURI* uri = aSheets[i]->GetSheetURI(); |
|
4273 |
|
4274 if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual) |
|
4275 return i; |
|
4276 } |
|
4277 |
|
4278 return -1; |
|
4279 } |
|
4280 |
|
4281 nsresult |
|
4282 nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) |
|
4283 { |
|
4284 NS_PRECONDITION(aSheetURI, "null arg"); |
|
4285 |
|
4286 // Checking if we have loaded this one already. |
|
4287 if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0) |
|
4288 return NS_ERROR_INVALID_ARG; |
|
4289 |
|
4290 // Loading the sheet sync. |
|
4291 nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader(); |
|
4292 |
|
4293 nsRefPtr<nsCSSStyleSheet> sheet; |
|
4294 nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet, |
|
4295 true, getter_AddRefs(sheet)); |
|
4296 NS_ENSURE_SUCCESS(rv, rv); |
|
4297 |
|
4298 mAdditionalSheets[aType].AppendObject(sheet); |
|
4299 sheet->SetOwningDocument(this); |
|
4300 MOZ_ASSERT(sheet->IsApplicable()); |
|
4301 |
|
4302 BeginUpdate(UPDATE_STYLE); |
|
4303 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
4304 if (shell) { |
|
4305 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); |
|
4306 shell->StyleSet()->AppendStyleSheet(type, sheet); |
|
4307 } |
|
4308 |
|
4309 // Passing false, so documet.styleSheets.length will not be affected by |
|
4310 // these additional sheets. |
|
4311 NotifyStyleSheetAdded(sheet, false); |
|
4312 EndUpdate(UPDATE_STYLE); |
|
4313 |
|
4314 return NS_OK; |
|
4315 } |
|
4316 |
|
4317 void |
|
4318 nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) |
|
4319 { |
|
4320 MOZ_ASSERT(aSheetURI); |
|
4321 |
|
4322 nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType]; |
|
4323 |
|
4324 int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI); |
|
4325 if (i >= 0) { |
|
4326 nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i]; |
|
4327 sheets.RemoveObjectAt(i); |
|
4328 |
|
4329 BeginUpdate(UPDATE_STYLE); |
|
4330 if (!mIsGoingAway) { |
|
4331 MOZ_ASSERT(sheetRef->IsApplicable()); |
|
4332 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
4333 if (shell) { |
|
4334 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); |
|
4335 shell->StyleSet()->RemoveStyleSheet(type, sheetRef); |
|
4336 } |
|
4337 } |
|
4338 |
|
4339 // Passing false, so documet.styleSheets.length will not be affected by |
|
4340 // these additional sheets. |
|
4341 NotifyStyleSheetRemoved(sheetRef, false); |
|
4342 EndUpdate(UPDATE_STYLE); |
|
4343 |
|
4344 sheetRef->SetOwningDocument(nullptr); |
|
4345 } |
|
4346 } |
|
4347 |
|
4348 nsIStyleSheet* |
|
4349 nsDocument::FirstAdditionalAuthorSheet() |
|
4350 { |
|
4351 return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0); |
|
4352 } |
|
4353 |
|
4354 nsIGlobalObject* |
|
4355 nsDocument::GetScopeObject() const |
|
4356 { |
|
4357 nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject)); |
|
4358 return scope; |
|
4359 } |
|
4360 |
|
4361 void |
|
4362 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal) |
|
4363 { |
|
4364 mScopeObject = do_GetWeakReference(aGlobal); |
|
4365 if (aGlobal) { |
|
4366 mHasHadScriptHandlingObject = true; |
|
4367 } |
|
4368 } |
|
4369 |
|
4370 static void |
|
4371 NotifyActivityChanged(nsIContent *aContent, void *aUnused) |
|
4372 { |
|
4373 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent)); |
|
4374 if (domMediaElem) { |
|
4375 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(aContent); |
|
4376 mediaElem->NotifyOwnerDocumentActivityChanged(); |
|
4377 } |
|
4378 nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aContent)); |
|
4379 if (objectLoadingContent) { |
|
4380 nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get()); |
|
4381 olc->NotifyOwnerDocumentActivityChanged(); |
|
4382 } |
|
4383 } |
|
4384 |
|
4385 void |
|
4386 nsIDocument::SetContainer(nsDocShell* aContainer) |
|
4387 { |
|
4388 if (aContainer) { |
|
4389 mDocumentContainer = aContainer->asWeakPtr(); |
|
4390 } else { |
|
4391 mDocumentContainer = WeakPtr<nsDocShell>(); |
|
4392 } |
|
4393 |
|
4394 EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
|
4395 if (!aContainer) { |
|
4396 return; |
|
4397 } |
|
4398 |
|
4399 // Get the Docshell |
|
4400 if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) { |
|
4401 // check if same type root |
|
4402 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; |
|
4403 aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); |
|
4404 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); |
|
4405 |
|
4406 if (sameTypeRoot == aContainer) { |
|
4407 static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true); |
|
4408 } |
|
4409 } |
|
4410 } |
|
4411 |
|
4412 nsISupports* |
|
4413 nsIDocument::GetContainer() const |
|
4414 { |
|
4415 return static_cast<nsIDocShell*>(mDocumentContainer); |
|
4416 } |
|
4417 |
|
4418 void |
|
4419 nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) |
|
4420 { |
|
4421 #ifdef DEBUG |
|
4422 { |
|
4423 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject)); |
|
4424 |
|
4425 NS_ASSERTION(!win || win->IsInnerWindow(), |
|
4426 "Script global object must be an inner window!"); |
|
4427 } |
|
4428 #endif |
|
4429 NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController || |
|
4430 mAnimationController->IsPausedByType( |
|
4431 nsSMILTimeContainer::PAUSE_PAGEHIDE | |
|
4432 nsSMILTimeContainer::PAUSE_BEGIN), |
|
4433 "Clearing window pointer while animations are unpaused"); |
|
4434 |
|
4435 if (mScriptGlobalObject && !aScriptGlobalObject) { |
|
4436 // We're detaching from the window. We need to grab a pointer to |
|
4437 // our layout history state now. |
|
4438 mLayoutHistoryState = GetLayoutHistoryState(); |
|
4439 |
|
4440 if (mPresShell && !EventHandlingSuppressed()) { |
|
4441 RevokeAnimationFrameNotifications(); |
|
4442 } |
|
4443 |
|
4444 // Also make sure to remove our onload blocker now if we haven't done it yet |
|
4445 if (mOnloadBlockCount != 0) { |
|
4446 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
|
4447 if (loadGroup) { |
|
4448 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); |
|
4449 } |
|
4450 } |
|
4451 } |
|
4452 |
|
4453 mScriptGlobalObject = aScriptGlobalObject; |
|
4454 |
|
4455 if (aScriptGlobalObject) { |
|
4456 mHasHadScriptHandlingObject = true; |
|
4457 mHasHadDefaultView = true; |
|
4458 // Go back to using the docshell for the layout history state |
|
4459 mLayoutHistoryState = nullptr; |
|
4460 mScopeObject = do_GetWeakReference(aScriptGlobalObject); |
|
4461 #ifdef DEBUG |
|
4462 if (!mWillReparent) { |
|
4463 // We really shouldn't have a wrapper here but if we do we need to make sure |
|
4464 // it has the correct parent. |
|
4465 JSObject *obj = GetWrapperPreserveColor(); |
|
4466 if (obj) { |
|
4467 JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject(); |
|
4468 NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope, |
|
4469 "Wrong scope, this is really bad!"); |
|
4470 } |
|
4471 } |
|
4472 #endif |
|
4473 |
|
4474 if (mAllowDNSPrefetch) { |
|
4475 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
4476 if (docShell) { |
|
4477 #ifdef DEBUG |
|
4478 nsCOMPtr<nsIWebNavigation> webNav = |
|
4479 do_GetInterface(aScriptGlobalObject); |
|
4480 NS_ASSERTION(SameCOMIdentity(webNav, docShell), |
|
4481 "Unexpected container or script global?"); |
|
4482 #endif |
|
4483 bool allowDNSPrefetch; |
|
4484 docShell->GetAllowDNSPrefetch(&allowDNSPrefetch); |
|
4485 mAllowDNSPrefetch = allowDNSPrefetch; |
|
4486 } |
|
4487 } |
|
4488 |
|
4489 MaybeRescheduleAnimationFrameNotifications(); |
|
4490 mRegistry = new Registry(); |
|
4491 } |
|
4492 |
|
4493 // Remember the pointer to our window (or lack there of), to avoid |
|
4494 // having to QI every time it's asked for. |
|
4495 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject); |
|
4496 mWindow = window; |
|
4497 |
|
4498 // Now that we know what our window is, we can flush the CSP errors to the |
|
4499 // Web Console. We are flushing all messages that occured and were stored |
|
4500 // in the queue prior to this point. |
|
4501 FlushCSPWebConsoleErrorQueue(); |
|
4502 nsCOMPtr<nsIHttpChannelInternal> internalChannel = |
|
4503 do_QueryInterface(GetChannel()); |
|
4504 if (internalChannel) { |
|
4505 nsCOMArray<nsISecurityConsoleMessage> messages; |
|
4506 internalChannel->TakeAllSecurityMessages(messages); |
|
4507 SendToConsole(messages); |
|
4508 } |
|
4509 |
|
4510 // Set our visibility state, but do not fire the event. This is correct |
|
4511 // because either we're coming out of bfcache (in which case IsVisible() will |
|
4512 // still test false at this point and no state change will happen) or we're |
|
4513 // doing the initial document load and don't want to fire the event for this |
|
4514 // change. |
|
4515 mVisibilityState = GetVisibilityState(); |
|
4516 } |
|
4517 |
|
4518 nsIScriptGlobalObject* |
|
4519 nsDocument::GetScriptHandlingObjectInternal() const |
|
4520 { |
|
4521 MOZ_ASSERT(!mScriptGlobalObject, |
|
4522 "Do not call this when mScriptGlobalObject is set!"); |
|
4523 if (mHasHadDefaultView) { |
|
4524 return nullptr; |
|
4525 } |
|
4526 |
|
4527 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject = |
|
4528 do_QueryReferent(mScopeObject); |
|
4529 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject); |
|
4530 if (win) { |
|
4531 NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!"); |
|
4532 nsPIDOMWindow* outer = win->GetOuterWindow(); |
|
4533 if (!outer || outer->GetCurrentInnerWindow() != win) { |
|
4534 NS_WARNING("Wrong inner/outer window combination!"); |
|
4535 return nullptr; |
|
4536 } |
|
4537 } |
|
4538 return scriptHandlingObject; |
|
4539 } |
|
4540 void |
|
4541 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) |
|
4542 { |
|
4543 NS_ASSERTION(!mScriptGlobalObject || |
|
4544 mScriptGlobalObject == aScriptObject, |
|
4545 "Wrong script object!"); |
|
4546 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject); |
|
4547 NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!"); |
|
4548 if (aScriptObject) { |
|
4549 mScopeObject = do_GetWeakReference(aScriptObject); |
|
4550 mHasHadScriptHandlingObject = true; |
|
4551 mHasHadDefaultView = false; |
|
4552 } |
|
4553 } |
|
4554 |
|
4555 bool |
|
4556 nsDocument::IsTopLevelContentDocument() |
|
4557 { |
|
4558 return mIsTopLevelContentDocument; |
|
4559 } |
|
4560 |
|
4561 void |
|
4562 nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument) |
|
4563 { |
|
4564 mIsTopLevelContentDocument = aIsTopLevelContentDocument; |
|
4565 } |
|
4566 |
|
4567 nsPIDOMWindow * |
|
4568 nsDocument::GetWindowInternal() const |
|
4569 { |
|
4570 MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!"); |
|
4571 // Let's use mScriptGlobalObject. Even if the document is already removed from |
|
4572 // the docshell, the outer window might be still obtainable from the it. |
|
4573 nsCOMPtr<nsPIDOMWindow> win; |
|
4574 if (mRemovedFromDocShell) { |
|
4575 nsCOMPtr<nsIInterfaceRequestor> requestor(mDocumentContainer); |
|
4576 if (requestor) { |
|
4577 // The docshell returns the outer window we are done. |
|
4578 win = do_GetInterface(requestor); |
|
4579 } |
|
4580 } else { |
|
4581 win = do_QueryInterface(mScriptGlobalObject); |
|
4582 if (win) { |
|
4583 // mScriptGlobalObject is always the inner window, let's get the outer. |
|
4584 win = win->GetOuterWindow(); |
|
4585 } |
|
4586 } |
|
4587 |
|
4588 return win; |
|
4589 } |
|
4590 |
|
4591 nsScriptLoader* |
|
4592 nsDocument::ScriptLoader() |
|
4593 { |
|
4594 return mScriptLoader; |
|
4595 } |
|
4596 |
|
4597 bool |
|
4598 nsDocument::InternalAllowXULXBL() |
|
4599 { |
|
4600 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) { |
|
4601 mAllowXULXBL = eTriTrue; |
|
4602 return true; |
|
4603 } |
|
4604 |
|
4605 mAllowXULXBL = eTriFalse; |
|
4606 return false; |
|
4607 } |
|
4608 |
|
4609 // Note: We don't hold a reference to the document observer; we assume |
|
4610 // that it has a live reference to the document. |
|
4611 void |
|
4612 nsDocument::AddObserver(nsIDocumentObserver* aObserver) |
|
4613 { |
|
4614 NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex, |
|
4615 "Observer already in the list"); |
|
4616 mObservers.AppendElement(aObserver); |
|
4617 AddMutationObserver(aObserver); |
|
4618 } |
|
4619 |
|
4620 bool |
|
4621 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver) |
|
4622 { |
|
4623 // If we're in the process of destroying the document (and we're |
|
4624 // informing the observers of the destruction), don't remove the |
|
4625 // observers from the list. This is not a big deal, since we |
|
4626 // don't hold a live reference to the observers. |
|
4627 if (!mInDestructor) { |
|
4628 RemoveMutationObserver(aObserver); |
|
4629 return mObservers.RemoveElement(aObserver); |
|
4630 } |
|
4631 |
|
4632 return mObservers.Contains(aObserver); |
|
4633 } |
|
4634 |
|
4635 void |
|
4636 nsDocument::MaybeEndOutermostXBLUpdate() |
|
4637 { |
|
4638 // Only call BindingManager()->EndOutermostUpdate() when |
|
4639 // we're not in an update and it is safe to run scripts. |
|
4640 if (mUpdateNestLevel == 0 && mInXBLUpdate) { |
|
4641 if (nsContentUtils::IsSafeToRunScript()) { |
|
4642 mInXBLUpdate = false; |
|
4643 BindingManager()->EndOutermostUpdate(); |
|
4644 } else if (!mInDestructor) { |
|
4645 nsContentUtils::AddScriptRunner( |
|
4646 NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate)); |
|
4647 } |
|
4648 } |
|
4649 } |
|
4650 |
|
4651 void |
|
4652 nsDocument::BeginUpdate(nsUpdateType aUpdateType) |
|
4653 { |
|
4654 if (mUpdateNestLevel == 0 && !mInXBLUpdate) { |
|
4655 mInXBLUpdate = true; |
|
4656 BindingManager()->BeginOutermostUpdate(); |
|
4657 } |
|
4658 |
|
4659 ++mUpdateNestLevel; |
|
4660 nsContentUtils::AddScriptBlocker(); |
|
4661 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType)); |
|
4662 } |
|
4663 |
|
4664 void |
|
4665 nsDocument::EndUpdate(nsUpdateType aUpdateType) |
|
4666 { |
|
4667 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType)); |
|
4668 |
|
4669 nsContentUtils::RemoveScriptBlocker(); |
|
4670 |
|
4671 --mUpdateNestLevel; |
|
4672 |
|
4673 // This set of updates may have created XBL bindings. Let the |
|
4674 // binding manager know we're done. |
|
4675 MaybeEndOutermostXBLUpdate(); |
|
4676 |
|
4677 MaybeInitializeFinalizeFrameLoaders(); |
|
4678 } |
|
4679 |
|
4680 void |
|
4681 nsDocument::BeginLoad() |
|
4682 { |
|
4683 // Block onload here to prevent having to deal with blocking and |
|
4684 // unblocking it while we know the document is loading. |
|
4685 BlockOnload(); |
|
4686 mDidFireDOMContentLoaded = false; |
|
4687 BlockDOMContentLoaded(); |
|
4688 |
|
4689 if (mScriptLoader) { |
|
4690 mScriptLoader->BeginDeferringScripts(); |
|
4691 } |
|
4692 |
|
4693 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this)); |
|
4694 } |
|
4695 |
|
4696 void |
|
4697 nsDocument::ReportEmptyGetElementByIdArg() |
|
4698 { |
|
4699 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
4700 NS_LITERAL_CSTRING("DOM"), this, |
|
4701 nsContentUtils::eDOM_PROPERTIES, |
|
4702 "EmptyGetElementByIdParam"); |
|
4703 } |
|
4704 |
|
4705 Element* |
|
4706 nsDocument::GetElementById(const nsAString& aElementId) |
|
4707 { |
|
4708 if (!CheckGetElementByIdArg(aElementId)) { |
|
4709 return nullptr; |
|
4710 } |
|
4711 |
|
4712 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); |
|
4713 return entry ? entry->GetIdElement() : nullptr; |
|
4714 } |
|
4715 |
|
4716 const nsSmallVoidArray* |
|
4717 nsDocument::GetAllElementsForId(const nsAString& aElementId) const |
|
4718 { |
|
4719 if (aElementId.IsEmpty()) { |
|
4720 return nullptr; |
|
4721 } |
|
4722 |
|
4723 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); |
|
4724 return entry ? entry->GetIdElements() : nullptr; |
|
4725 } |
|
4726 |
|
4727 NS_IMETHODIMP |
|
4728 nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn) |
|
4729 { |
|
4730 Element *content = GetElementById(aId); |
|
4731 if (content) { |
|
4732 return CallQueryInterface(content, aReturn); |
|
4733 } |
|
4734 |
|
4735 *aReturn = nullptr; |
|
4736 |
|
4737 return NS_OK; |
|
4738 } |
|
4739 |
|
4740 Element* |
|
4741 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, |
|
4742 void* aData, bool aForImage) |
|
4743 { |
|
4744 nsDependentAtomString id(aID); |
|
4745 |
|
4746 if (!CheckGetElementByIdArg(id)) |
|
4747 return nullptr; |
|
4748 |
|
4749 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id); |
|
4750 NS_ENSURE_TRUE(entry, nullptr); |
|
4751 |
|
4752 entry->AddContentChangeCallback(aObserver, aData, aForImage); |
|
4753 return aForImage ? entry->GetImageIdElement() : entry->GetIdElement(); |
|
4754 } |
|
4755 |
|
4756 void |
|
4757 nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, |
|
4758 void* aData, bool aForImage) |
|
4759 { |
|
4760 nsDependentAtomString id(aID); |
|
4761 |
|
4762 if (!CheckGetElementByIdArg(id)) |
|
4763 return; |
|
4764 |
|
4765 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id); |
|
4766 if (!entry) { |
|
4767 return; |
|
4768 } |
|
4769 |
|
4770 entry->RemoveContentChangeCallback(aObserver, aData, aForImage); |
|
4771 } |
|
4772 |
|
4773 NS_IMETHODIMP |
|
4774 nsDocument::MozSetImageElement(const nsAString& aImageElementId, |
|
4775 nsIDOMElement* aImageElement) |
|
4776 { |
|
4777 nsCOMPtr<Element> el = do_QueryInterface(aImageElement); |
|
4778 MozSetImageElement(aImageElementId, el); |
|
4779 return NS_OK; |
|
4780 } |
|
4781 |
|
4782 void |
|
4783 nsDocument::MozSetImageElement(const nsAString& aImageElementId, |
|
4784 Element* aElement) |
|
4785 { |
|
4786 if (aImageElementId.IsEmpty()) |
|
4787 return; |
|
4788 |
|
4789 // Hold a script blocker while calling SetImageElement since that can call |
|
4790 // out to id-observers |
|
4791 nsAutoScriptBlocker scriptBlocker; |
|
4792 |
|
4793 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId); |
|
4794 if (entry) { |
|
4795 entry->SetImageElement(aElement); |
|
4796 if (entry->IsEmpty()) { |
|
4797 mIdentifierMap.RemoveEntry(aImageElementId); |
|
4798 } |
|
4799 } |
|
4800 } |
|
4801 |
|
4802 Element* |
|
4803 nsDocument::LookupImageElement(const nsAString& aId) |
|
4804 { |
|
4805 if (aId.IsEmpty()) |
|
4806 return nullptr; |
|
4807 |
|
4808 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); |
|
4809 return entry ? entry->GetImageIdElement() : nullptr; |
|
4810 } |
|
4811 |
|
4812 void |
|
4813 nsDocument::DispatchContentLoadedEvents() |
|
4814 { |
|
4815 // If you add early returns from this method, make sure you're |
|
4816 // calling UnblockOnload properly. |
|
4817 |
|
4818 // Unpin references to preloaded images |
|
4819 mPreloadingImages.Clear(); |
|
4820 |
|
4821 if (mTiming) { |
|
4822 mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI()); |
|
4823 } |
|
4824 |
|
4825 // Dispatch observer notification to notify observers document is interactive. |
|
4826 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
4827 nsIPrincipal *principal = GetPrincipal(); |
|
4828 os->NotifyObservers(static_cast<nsIDocument*>(this), |
|
4829 nsContentUtils::IsSystemPrincipal(principal) ? |
|
4830 "chrome-document-interactive" : |
|
4831 "content-document-interactive", |
|
4832 nullptr); |
|
4833 |
|
4834 // Fire a DOM event notifying listeners that this document has been |
|
4835 // loaded (excluding images and other loads initiated by this |
|
4836 // document). |
|
4837 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
|
4838 NS_LITERAL_STRING("DOMContentLoaded"), |
|
4839 true, true); |
|
4840 |
|
4841 if (mTiming) { |
|
4842 mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI()); |
|
4843 } |
|
4844 |
|
4845 // If this document is a [i]frame, fire a DOMFrameContentLoaded |
|
4846 // event on all parent documents notifying that the HTML (excluding |
|
4847 // other external files such as images and stylesheets) in a frame |
|
4848 // has finished loading. |
|
4849 |
|
4850 // target_frame is the [i]frame element that will be used as the |
|
4851 // target for the event. It's the [i]frame whose content is done |
|
4852 // loading. |
|
4853 nsCOMPtr<EventTarget> target_frame; |
|
4854 |
|
4855 if (mParentDocument) { |
|
4856 target_frame = mParentDocument->FindContentForSubDocument(this); |
|
4857 } |
|
4858 |
|
4859 if (target_frame) { |
|
4860 nsCOMPtr<nsIDocument> parent = mParentDocument; |
|
4861 do { |
|
4862 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent); |
|
4863 |
|
4864 nsCOMPtr<nsIDOMEvent> event; |
|
4865 if (domDoc) { |
|
4866 domDoc->CreateEvent(NS_LITERAL_STRING("Events"), |
|
4867 getter_AddRefs(event)); |
|
4868 |
|
4869 } |
|
4870 |
|
4871 if (event) { |
|
4872 event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true, |
|
4873 true); |
|
4874 |
|
4875 event->SetTarget(target_frame); |
|
4876 event->SetTrusted(true); |
|
4877 |
|
4878 // To dispatch this event we must manually call |
|
4879 // EventDispatcher::Dispatch() on the ancestor document since the |
|
4880 // target is not in the same document, so the event would never reach |
|
4881 // the ancestor document if we used the normal event |
|
4882 // dispatching code. |
|
4883 |
|
4884 WidgetEvent* innerEvent = event->GetInternalNSEvent(); |
|
4885 if (innerEvent) { |
|
4886 nsEventStatus status = nsEventStatus_eIgnore; |
|
4887 |
|
4888 nsIPresShell *shell = parent->GetShell(); |
|
4889 if (shell) { |
|
4890 nsRefPtr<nsPresContext> context = shell->GetPresContext(); |
|
4891 |
|
4892 if (context) { |
|
4893 EventDispatcher::Dispatch(parent, context, innerEvent, event, |
|
4894 &status); |
|
4895 } |
|
4896 } |
|
4897 } |
|
4898 } |
|
4899 |
|
4900 parent = parent->GetParentDocument(); |
|
4901 } while (parent); |
|
4902 } |
|
4903 |
|
4904 // If the document has a manifest attribute, fire a MozApplicationManifest |
|
4905 // event. |
|
4906 Element* root = GetRootElement(); |
|
4907 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) { |
|
4908 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this), |
|
4909 NS_LITERAL_STRING("MozApplicationManifest"), |
|
4910 true, true); |
|
4911 } |
|
4912 |
|
4913 UnblockOnload(true); |
|
4914 } |
|
4915 |
|
4916 void |
|
4917 nsDocument::EndLoad() |
|
4918 { |
|
4919 // Drop the ref to our parser, if any, but keep hold of the sink so that we |
|
4920 // can flush it from FlushPendingNotifications as needed. We might have to |
|
4921 // do that to get a StartLayout() to happen. |
|
4922 if (mParser) { |
|
4923 mWeakSink = do_GetWeakReference(mParser->GetContentSink()); |
|
4924 mParser = nullptr; |
|
4925 } |
|
4926 |
|
4927 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); |
|
4928 |
|
4929 UnblockDOMContentLoaded(); |
|
4930 } |
|
4931 |
|
4932 void |
|
4933 nsDocument::UnblockDOMContentLoaded() |
|
4934 { |
|
4935 MOZ_ASSERT(mBlockDOMContentLoaded); |
|
4936 if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) { |
|
4937 return; |
|
4938 } |
|
4939 mDidFireDOMContentLoaded = true; |
|
4940 |
|
4941 MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); |
|
4942 if (!mSynchronousDOMContentLoaded) { |
|
4943 nsRefPtr<nsIRunnable> ev = |
|
4944 NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); |
|
4945 NS_DispatchToCurrentThread(ev); |
|
4946 } else { |
|
4947 DispatchContentLoadedEvents(); |
|
4948 } |
|
4949 } |
|
4950 |
|
4951 void |
|
4952 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) |
|
4953 { |
|
4954 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(), |
|
4955 "Someone forgot a scriptblocker"); |
|
4956 NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged, |
|
4957 (this, aContent, aStateMask)); |
|
4958 } |
|
4959 |
|
4960 void |
|
4961 nsDocument::DocumentStatesChanged(EventStates aStateMask) |
|
4962 { |
|
4963 // Invalidate our cached state. |
|
4964 mGotDocumentState &= ~aStateMask; |
|
4965 mDocumentState &= ~aStateMask; |
|
4966 |
|
4967 NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask)); |
|
4968 } |
|
4969 |
|
4970 void |
|
4971 nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet, |
|
4972 nsIStyleRule* aOldStyleRule, |
|
4973 nsIStyleRule* aNewStyleRule) |
|
4974 { |
|
4975 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, |
|
4976 (this, aSheet, |
|
4977 aOldStyleRule, aNewStyleRule)); |
|
4978 |
|
4979 if (StyleSheetChangeEventsEnabled()) { |
|
4980 nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule); |
|
4981 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent, |
|
4982 nsIDOMStyleRuleChangeEvent, |
|
4983 InitStyleRuleChangeEvent, |
|
4984 "StyleRuleChanged", |
|
4985 rule ? rule->GetDOMRule() : nullptr); |
|
4986 } |
|
4987 } |
|
4988 |
|
4989 void |
|
4990 nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet, |
|
4991 nsIStyleRule* aStyleRule) |
|
4992 { |
|
4993 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, |
|
4994 (this, aSheet, aStyleRule)); |
|
4995 |
|
4996 if (StyleSheetChangeEventsEnabled()) { |
|
4997 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule); |
|
4998 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent, |
|
4999 nsIDOMStyleRuleChangeEvent, |
|
5000 InitStyleRuleChangeEvent, |
|
5001 "StyleRuleAdded", |
|
5002 rule ? rule->GetDOMRule() : nullptr); |
|
5003 } |
|
5004 } |
|
5005 |
|
5006 void |
|
5007 nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet, |
|
5008 nsIStyleRule* aStyleRule) |
|
5009 { |
|
5010 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, |
|
5011 (this, aSheet, aStyleRule)); |
|
5012 |
|
5013 if (StyleSheetChangeEventsEnabled()) { |
|
5014 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule); |
|
5015 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent, |
|
5016 nsIDOMStyleRuleChangeEvent, |
|
5017 InitStyleRuleChangeEvent, |
|
5018 "StyleRuleRemoved", |
|
5019 rule ? rule->GetDOMRule() : nullptr); |
|
5020 } |
|
5021 } |
|
5022 |
|
5023 #undef DO_STYLESHEET_NOTIFICATION |
|
5024 |
|
5025 |
|
5026 // |
|
5027 // nsIDOMDocument interface |
|
5028 // |
|
5029 DocumentType* |
|
5030 nsIDocument::GetDoctype() const |
|
5031 { |
|
5032 for (nsIContent* child = GetFirstChild(); |
|
5033 child; |
|
5034 child = child->GetNextSibling()) { |
|
5035 if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { |
|
5036 return static_cast<DocumentType*>(child); |
|
5037 } |
|
5038 } |
|
5039 return nullptr; |
|
5040 } |
|
5041 |
|
5042 NS_IMETHODIMP |
|
5043 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype) |
|
5044 { |
|
5045 MOZ_ASSERT(aDoctype); |
|
5046 nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype(); |
|
5047 doctype.forget(aDoctype); |
|
5048 return NS_OK; |
|
5049 } |
|
5050 |
|
5051 NS_IMETHODIMP |
|
5052 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation) |
|
5053 { |
|
5054 ErrorResult rv; |
|
5055 *aImplementation = GetImplementation(rv); |
|
5056 if (rv.Failed()) { |
|
5057 MOZ_ASSERT(!*aImplementation); |
|
5058 return rv.ErrorCode(); |
|
5059 } |
|
5060 NS_ADDREF(*aImplementation); |
|
5061 return NS_OK; |
|
5062 } |
|
5063 |
|
5064 DOMImplementation* |
|
5065 nsDocument::GetImplementation(ErrorResult& rv) |
|
5066 { |
|
5067 if (!mDOMImplementation) { |
|
5068 nsCOMPtr<nsIURI> uri; |
|
5069 NS_NewURI(getter_AddRefs(uri), "about:blank"); |
|
5070 if (!uri) { |
|
5071 rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
5072 return nullptr; |
|
5073 } |
|
5074 bool hasHadScriptObject = true; |
|
5075 nsIScriptGlobalObject* scriptObject = |
|
5076 GetScriptHandlingObject(hasHadScriptObject); |
|
5077 if (!scriptObject && hasHadScriptObject) { |
|
5078 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5079 return nullptr; |
|
5080 } |
|
5081 mDOMImplementation = new DOMImplementation(this, |
|
5082 scriptObject ? scriptObject : GetScopeObject(), uri, uri); |
|
5083 } |
|
5084 |
|
5085 return mDOMImplementation; |
|
5086 } |
|
5087 |
|
5088 NS_IMETHODIMP |
|
5089 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement) |
|
5090 { |
|
5091 NS_ENSURE_ARG_POINTER(aDocumentElement); |
|
5092 |
|
5093 Element* root = GetRootElement(); |
|
5094 if (root) { |
|
5095 return CallQueryInterface(root, aDocumentElement); |
|
5096 } |
|
5097 |
|
5098 *aDocumentElement = nullptr; |
|
5099 |
|
5100 return NS_OK; |
|
5101 } |
|
5102 |
|
5103 NS_IMETHODIMP |
|
5104 nsDocument::CreateElement(const nsAString& aTagName, |
|
5105 nsIDOMElement** aReturn) |
|
5106 { |
|
5107 *aReturn = nullptr; |
|
5108 ErrorResult rv; |
|
5109 nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv); |
|
5110 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode()); |
|
5111 return CallQueryInterface(element, aReturn); |
|
5112 } |
|
5113 |
|
5114 bool IsLowercaseASCII(const nsAString& aValue) |
|
5115 { |
|
5116 int32_t len = aValue.Length(); |
|
5117 for (int32_t i = 0; i < len; ++i) { |
|
5118 char16_t c = aValue[i]; |
|
5119 if (!(0x0061 <= (c) && ((c) <= 0x007a))) { |
|
5120 return false; |
|
5121 } |
|
5122 } |
|
5123 return true; |
|
5124 } |
|
5125 |
|
5126 already_AddRefed<Element> |
|
5127 nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv) |
|
5128 { |
|
5129 rv = nsContentUtils::CheckQName(aTagName, false); |
|
5130 if (rv.Failed()) { |
|
5131 return nullptr; |
|
5132 } |
|
5133 |
|
5134 bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName); |
|
5135 nsAutoString lcTagName; |
|
5136 if (needsLowercase) { |
|
5137 nsContentUtils::ASCIIToLower(aTagName, lcTagName); |
|
5138 } |
|
5139 |
|
5140 nsCOMPtr<nsIContent> content; |
|
5141 rv = CreateElem(needsLowercase ? lcTagName : aTagName, |
|
5142 nullptr, mDefaultElementType, getter_AddRefs(content)); |
|
5143 if (rv.Failed()) { |
|
5144 return nullptr; |
|
5145 } |
|
5146 return dont_AddRef(content.forget().take()->AsElement()); |
|
5147 } |
|
5148 |
|
5149 void |
|
5150 nsDocument::SwizzleCustomElement(Element* aElement, |
|
5151 const nsAString& aTypeExtension, |
|
5152 uint32_t aNamespaceID, |
|
5153 ErrorResult& rv) |
|
5154 { |
|
5155 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension)); |
|
5156 nsCOMPtr<nsIAtom> tagAtom = aElement->Tag(); |
|
5157 if (!mRegistry || tagAtom == typeAtom) { |
|
5158 return; |
|
5159 } |
|
5160 |
|
5161 CustomElementDefinition* data; |
|
5162 CustomElementHashKey key(aNamespaceID, typeAtom); |
|
5163 if (!mRegistry->mCustomDefinitions.Get(&key, &data)) { |
|
5164 // The type extension doesn't exist in the registry, |
|
5165 // thus we don't need to swizzle, but it is possibly |
|
5166 // an upgrade candidate. |
|
5167 RegisterUnresolvedElement(aElement, typeAtom); |
|
5168 return; |
|
5169 } |
|
5170 |
|
5171 if (data->mLocalName != tagAtom) { |
|
5172 // The element doesn't match the local name for the |
|
5173 // definition, thus the element isn't a custom element |
|
5174 // and we don't need to do anything more. |
|
5175 return; |
|
5176 } |
|
5177 |
|
5178 if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) { |
|
5179 // Swizzling in the parser happens after the "is" attribute is added. |
|
5180 aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true); |
|
5181 } |
|
5182 |
|
5183 // Enqueuing the created callback will set the CustomElementData on the |
|
5184 // element, causing prototype swizzling to occur in Element::WrapObject. |
|
5185 EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data); |
|
5186 } |
|
5187 |
|
5188 already_AddRefed<Element> |
|
5189 nsDocument::CreateElement(const nsAString& aTagName, |
|
5190 const nsAString& aTypeExtension, |
|
5191 ErrorResult& rv) |
|
5192 { |
|
5193 nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv); |
|
5194 if (rv.Failed()) { |
|
5195 return nullptr; |
|
5196 } |
|
5197 |
|
5198 SwizzleCustomElement(elem, aTypeExtension, |
|
5199 GetDefaultNamespaceID(), rv); |
|
5200 if (rv.Failed()) { |
|
5201 return nullptr; |
|
5202 } |
|
5203 |
|
5204 return elem.forget(); |
|
5205 } |
|
5206 |
|
5207 NS_IMETHODIMP |
|
5208 nsDocument::CreateElementNS(const nsAString& aNamespaceURI, |
|
5209 const nsAString& aQualifiedName, |
|
5210 nsIDOMElement** aReturn) |
|
5211 { |
|
5212 *aReturn = nullptr; |
|
5213 ErrorResult rv; |
|
5214 nsCOMPtr<Element> element = |
|
5215 nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv); |
|
5216 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode()); |
|
5217 return CallQueryInterface(element, aReturn); |
|
5218 } |
|
5219 |
|
5220 already_AddRefed<Element> |
|
5221 nsIDocument::CreateElementNS(const nsAString& aNamespaceURI, |
|
5222 const nsAString& aQualifiedName, |
|
5223 ErrorResult& rv) |
|
5224 { |
|
5225 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
5226 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, |
|
5227 aQualifiedName, |
|
5228 mNodeInfoManager, |
|
5229 nsIDOMNode::ELEMENT_NODE, |
|
5230 getter_AddRefs(nodeInfo)); |
|
5231 if (rv.Failed()) { |
|
5232 return nullptr; |
|
5233 } |
|
5234 |
|
5235 nsCOMPtr<Element> element; |
|
5236 rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), |
|
5237 NOT_FROM_PARSER); |
|
5238 if (rv.Failed()) { |
|
5239 return nullptr; |
|
5240 } |
|
5241 return element.forget(); |
|
5242 } |
|
5243 |
|
5244 already_AddRefed<Element> |
|
5245 nsDocument::CreateElementNS(const nsAString& aNamespaceURI, |
|
5246 const nsAString& aQualifiedName, |
|
5247 const nsAString& aTypeExtension, |
|
5248 ErrorResult& rv) |
|
5249 { |
|
5250 nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI, |
|
5251 aQualifiedName, |
|
5252 rv); |
|
5253 if (rv.Failed()) { |
|
5254 return nullptr; |
|
5255 } |
|
5256 |
|
5257 int32_t nameSpaceId = kNameSpaceID_Wildcard; |
|
5258 if (!aNamespaceURI.EqualsLiteral("*")) { |
|
5259 rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
|
5260 nameSpaceId); |
|
5261 if (rv.Failed()) { |
|
5262 return nullptr; |
|
5263 } |
|
5264 } |
|
5265 |
|
5266 SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv); |
|
5267 if (rv.Failed()) { |
|
5268 return nullptr; |
|
5269 } |
|
5270 |
|
5271 return elem.forget(); |
|
5272 } |
|
5273 |
|
5274 NS_IMETHODIMP |
|
5275 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn) |
|
5276 { |
|
5277 *aReturn = nsIDocument::CreateTextNode(aData).take(); |
|
5278 return NS_OK; |
|
5279 } |
|
5280 |
|
5281 already_AddRefed<nsTextNode> |
|
5282 nsIDocument::CreateTextNode(const nsAString& aData) const |
|
5283 { |
|
5284 nsRefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager); |
|
5285 // Don't notify; this node is still being created. |
|
5286 text->SetText(aData, false); |
|
5287 return text.forget(); |
|
5288 } |
|
5289 |
|
5290 NS_IMETHODIMP |
|
5291 nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn) |
|
5292 { |
|
5293 *aReturn = nsIDocument::CreateDocumentFragment().take(); |
|
5294 return NS_OK; |
|
5295 } |
|
5296 |
|
5297 already_AddRefed<DocumentFragment> |
|
5298 nsIDocument::CreateDocumentFragment() const |
|
5299 { |
|
5300 nsRefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager); |
|
5301 return frag.forget(); |
|
5302 } |
|
5303 |
|
5304 NS_IMETHODIMP |
|
5305 nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn) |
|
5306 { |
|
5307 *aReturn = nsIDocument::CreateComment(aData).take(); |
|
5308 return NS_OK; |
|
5309 } |
|
5310 |
|
5311 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers. |
|
5312 already_AddRefed<dom::Comment> |
|
5313 nsIDocument::CreateComment(const nsAString& aData) const |
|
5314 { |
|
5315 nsRefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager); |
|
5316 |
|
5317 // Don't notify; this node is still being created. |
|
5318 comment->SetText(aData, false); |
|
5319 return comment.forget(); |
|
5320 } |
|
5321 |
|
5322 NS_IMETHODIMP |
|
5323 nsDocument::CreateCDATASection(const nsAString& aData, |
|
5324 nsIDOMCDATASection** aReturn) |
|
5325 { |
|
5326 NS_ENSURE_ARG_POINTER(aReturn); |
|
5327 ErrorResult rv; |
|
5328 *aReturn = nsIDocument::CreateCDATASection(aData, rv).take(); |
|
5329 return rv.ErrorCode(); |
|
5330 } |
|
5331 |
|
5332 already_AddRefed<CDATASection> |
|
5333 nsIDocument::CreateCDATASection(const nsAString& aData, |
|
5334 ErrorResult& rv) |
|
5335 { |
|
5336 if (IsHTML()) { |
|
5337 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
5338 return nullptr; |
|
5339 } |
|
5340 |
|
5341 if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) { |
|
5342 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); |
|
5343 return nullptr; |
|
5344 } |
|
5345 |
|
5346 nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager); |
|
5347 |
|
5348 // Don't notify; this node is still being created. |
|
5349 cdata->SetText(aData, false); |
|
5350 |
|
5351 return cdata.forget(); |
|
5352 } |
|
5353 |
|
5354 NS_IMETHODIMP |
|
5355 nsDocument::CreateProcessingInstruction(const nsAString& aTarget, |
|
5356 const nsAString& aData, |
|
5357 nsIDOMProcessingInstruction** aReturn) |
|
5358 { |
|
5359 ErrorResult rv; |
|
5360 *aReturn = |
|
5361 nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take(); |
|
5362 return rv.ErrorCode(); |
|
5363 } |
|
5364 |
|
5365 already_AddRefed<ProcessingInstruction> |
|
5366 nsIDocument::CreateProcessingInstruction(const nsAString& aTarget, |
|
5367 const nsAString& aData, |
|
5368 ErrorResult& rv) const |
|
5369 { |
|
5370 nsresult res = nsContentUtils::CheckQName(aTarget, false); |
|
5371 if (NS_FAILED(res)) { |
|
5372 rv.Throw(res); |
|
5373 return nullptr; |
|
5374 } |
|
5375 |
|
5376 if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) { |
|
5377 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); |
|
5378 return nullptr; |
|
5379 } |
|
5380 |
|
5381 nsRefPtr<ProcessingInstruction> pi = |
|
5382 NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData); |
|
5383 |
|
5384 return pi.forget(); |
|
5385 } |
|
5386 |
|
5387 NS_IMETHODIMP |
|
5388 nsDocument::CreateAttribute(const nsAString& aName, |
|
5389 nsIDOMAttr** aReturn) |
|
5390 { |
|
5391 ErrorResult rv; |
|
5392 *aReturn = nsIDocument::CreateAttribute(aName, rv).take(); |
|
5393 return rv.ErrorCode(); |
|
5394 } |
|
5395 |
|
5396 already_AddRefed<Attr> |
|
5397 nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv) |
|
5398 { |
|
5399 WarnOnceAbout(eCreateAttribute); |
|
5400 |
|
5401 if (!mNodeInfoManager) { |
|
5402 rv.Throw(NS_ERROR_NOT_INITIALIZED); |
|
5403 return nullptr; |
|
5404 } |
|
5405 |
|
5406 nsresult res = nsContentUtils::CheckQName(aName, false); |
|
5407 if (NS_FAILED(res)) { |
|
5408 rv.Throw(res); |
|
5409 return nullptr; |
|
5410 } |
|
5411 |
|
5412 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
5413 res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None, |
|
5414 nsIDOMNode::ATTRIBUTE_NODE, |
|
5415 getter_AddRefs(nodeInfo)); |
|
5416 if (NS_FAILED(res)) { |
|
5417 rv.Throw(res); |
|
5418 return nullptr; |
|
5419 } |
|
5420 |
|
5421 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), |
|
5422 EmptyString(), false); |
|
5423 return attribute.forget(); |
|
5424 } |
|
5425 |
|
5426 NS_IMETHODIMP |
|
5427 nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI, |
|
5428 const nsAString & aQualifiedName, |
|
5429 nsIDOMAttr **aResult) |
|
5430 { |
|
5431 ErrorResult rv; |
|
5432 *aResult = |
|
5433 nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take(); |
|
5434 return rv.ErrorCode(); |
|
5435 } |
|
5436 |
|
5437 already_AddRefed<Attr> |
|
5438 nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI, |
|
5439 const nsAString& aQualifiedName, |
|
5440 ErrorResult& rv) |
|
5441 { |
|
5442 WarnOnceAbout(eCreateAttributeNS); |
|
5443 |
|
5444 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
5445 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, |
|
5446 aQualifiedName, |
|
5447 mNodeInfoManager, |
|
5448 nsIDOMNode::ATTRIBUTE_NODE, |
|
5449 getter_AddRefs(nodeInfo)); |
|
5450 if (rv.Failed()) { |
|
5451 return nullptr; |
|
5452 } |
|
5453 |
|
5454 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), |
|
5455 EmptyString(), true); |
|
5456 return attribute.forget(); |
|
5457 } |
|
5458 |
|
5459 bool |
|
5460 nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp) |
|
5461 { |
|
5462 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); |
|
5463 |
|
5464 JS::Rooted<JSObject*> global(aCx, |
|
5465 JS_GetGlobalForObject(aCx, &args.callee())); |
|
5466 nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global); |
|
5467 MOZ_ASSERT(window, "Should have a non-null window"); |
|
5468 |
|
5469 nsDocument* document = static_cast<nsDocument*>(window->GetDoc()); |
|
5470 |
|
5471 // Function name is the type of the custom element. |
|
5472 JSString* jsFunName = |
|
5473 JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev())); |
|
5474 nsDependentJSString elemName; |
|
5475 if (!elemName.init(aCx, jsFunName)) { |
|
5476 return true; |
|
5477 } |
|
5478 |
|
5479 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName)); |
|
5480 CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom); |
|
5481 CustomElementDefinition* definition; |
|
5482 if (!document->mRegistry || |
|
5483 !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) { |
|
5484 return true; |
|
5485 } |
|
5486 |
|
5487 nsDependentAtomString localName(definition->mLocalName); |
|
5488 |
|
5489 nsCOMPtr<nsIContent> newElement; |
|
5490 nsresult rv = document->CreateElem(localName, nullptr, |
|
5491 definition->mNamespaceID, |
|
5492 getter_AddRefs(newElement)); |
|
5493 NS_ENSURE_SUCCESS(rv, true); |
|
5494 |
|
5495 ErrorResult errorResult; |
|
5496 nsCOMPtr<Element> element = do_QueryInterface(newElement); |
|
5497 document->SwizzleCustomElement(element, elemName, definition->mNamespaceID, |
|
5498 errorResult); |
|
5499 if (errorResult.Failed()) { |
|
5500 return true; |
|
5501 } |
|
5502 |
|
5503 rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval()); |
|
5504 NS_ENSURE_SUCCESS(rv, true); |
|
5505 |
|
5506 return true; |
|
5507 } |
|
5508 |
|
5509 bool |
|
5510 nsDocument::IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject) |
|
5511 { |
|
5512 JS::Rooted<JSObject*> obj(aCx, aObject); |
|
5513 return Preferences::GetBool("dom.webcomponents.enabled") || |
|
5514 IsInCertifiedApp(aCx, obj); |
|
5515 } |
|
5516 |
|
5517 nsresult |
|
5518 nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName) |
|
5519 { |
|
5520 if (!mRegistry) { |
|
5521 return NS_OK; |
|
5522 } |
|
5523 |
|
5524 nsINodeInfo* info = aElement->NodeInfo(); |
|
5525 |
|
5526 // Candidate may be a custom element through extension, |
|
5527 // in which case the custom element type name will not |
|
5528 // match the element tag name. e.g. <button is="x-button">. |
|
5529 nsCOMPtr<nsIAtom> typeName = aTypeName; |
|
5530 if (!typeName) { |
|
5531 typeName = info->NameAtom(); |
|
5532 } |
|
5533 |
|
5534 CustomElementHashKey key(info->NamespaceID(), typeName); |
|
5535 if (mRegistry->mCustomDefinitions.Get(&key)) { |
|
5536 return NS_OK; |
|
5537 } |
|
5538 |
|
5539 nsTArray<nsRefPtr<Element>>* unresolved; |
|
5540 mRegistry->mCandidatesMap.Get(&key, &unresolved); |
|
5541 if (!unresolved) { |
|
5542 unresolved = new nsTArray<nsRefPtr<Element>>(); |
|
5543 // Ownership of unresolved is taken by mCandidatesMap. |
|
5544 mRegistry->mCandidatesMap.Put(&key, unresolved); |
|
5545 } |
|
5546 |
|
5547 nsRefPtr<Element>* elem = unresolved->AppendElement(); |
|
5548 *elem = aElement; |
|
5549 |
|
5550 return NS_OK; |
|
5551 } |
|
5552 |
|
5553 namespace { |
|
5554 |
|
5555 class ProcessStackRunner MOZ_FINAL : public nsIRunnable |
|
5556 { |
|
5557 public: |
|
5558 ProcessStackRunner(bool aIsBaseQueue = false) |
|
5559 : mIsBaseQueue(aIsBaseQueue) |
|
5560 { |
|
5561 } |
|
5562 NS_DECL_ISUPPORTS |
|
5563 NS_IMETHOD Run() MOZ_OVERRIDE |
|
5564 { |
|
5565 nsDocument::ProcessTopElementQueue(mIsBaseQueue); |
|
5566 return NS_OK; |
|
5567 } |
|
5568 bool mIsBaseQueue; |
|
5569 }; |
|
5570 |
|
5571 NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable); |
|
5572 |
|
5573 } // anonymous namespace |
|
5574 |
|
5575 void |
|
5576 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, |
|
5577 Element* aCustomElement, |
|
5578 LifecycleCallbackArgs* aArgs, |
|
5579 CustomElementDefinition* aDefinition) |
|
5580 { |
|
5581 if (!mRegistry) { |
|
5582 // The element might not belong to a document that |
|
5583 // has a browsing context, and thus no registry. |
|
5584 return; |
|
5585 } |
|
5586 |
|
5587 CustomElementData* elementData = aCustomElement->GetCustomElementData(); |
|
5588 |
|
5589 // Let DEFINITION be ELEMENT's definition |
|
5590 CustomElementDefinition* definition = aDefinition; |
|
5591 if (!definition) { |
|
5592 nsINodeInfo* info = aCustomElement->NodeInfo(); |
|
5593 |
|
5594 // Make sure we get the correct definition in case the element |
|
5595 // is a extended custom element e.g. <button is="x-button">. |
|
5596 nsCOMPtr<nsIAtom> typeAtom = elementData ? |
|
5597 elementData->mType.get() : info->NameAtom(); |
|
5598 |
|
5599 CustomElementHashKey key(info->NamespaceID(), typeAtom); |
|
5600 if (!mRegistry->mCustomDefinitions.Get(&key, &definition) || |
|
5601 definition->mLocalName != info->NameAtom()) { |
|
5602 // Trying to enqueue a callback for an element that is not |
|
5603 // a custom element. We are done, nothing to do. |
|
5604 return; |
|
5605 } |
|
5606 } |
|
5607 |
|
5608 if (!elementData) { |
|
5609 // Create the custom element data the first time |
|
5610 // that we try to enqueue a callback. |
|
5611 elementData = new CustomElementData(definition->mType); |
|
5612 // aCustomElement takes ownership of elementData |
|
5613 aCustomElement->SetCustomElementData(elementData); |
|
5614 MOZ_ASSERT(aType == nsIDocument::eCreated, |
|
5615 "First callback should be the created callback"); |
|
5616 } |
|
5617 |
|
5618 // Let CALLBACK be the callback associated with the key NAME in CALLBACKS. |
|
5619 CallbackFunction* func = nullptr; |
|
5620 switch (aType) { |
|
5621 case nsIDocument::eCreated: |
|
5622 if (definition->mCallbacks->mCreatedCallback.WasPassed()) { |
|
5623 func = definition->mCallbacks->mCreatedCallback.Value(); |
|
5624 } |
|
5625 break; |
|
5626 |
|
5627 case nsIDocument::eAttached: |
|
5628 if (definition->mCallbacks->mAttachedCallback.WasPassed()) { |
|
5629 func = definition->mCallbacks->mAttachedCallback.Value(); |
|
5630 } |
|
5631 break; |
|
5632 |
|
5633 case nsIDocument::eDetached: |
|
5634 if (definition->mCallbacks->mDetachedCallback.WasPassed()) { |
|
5635 func = definition->mCallbacks->mDetachedCallback.Value(); |
|
5636 } |
|
5637 break; |
|
5638 |
|
5639 case nsIDocument::eAttributeChanged: |
|
5640 if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) { |
|
5641 func = definition->mCallbacks->mAttributeChangedCallback.Value(); |
|
5642 } |
|
5643 break; |
|
5644 } |
|
5645 |
|
5646 // If there is no such callback, stop. |
|
5647 if (!func) { |
|
5648 return; |
|
5649 } |
|
5650 |
|
5651 if (aType == nsIDocument::eCreated) { |
|
5652 elementData->mCreatedCallbackInvoked = false; |
|
5653 } else if (!elementData->mCreatedCallbackInvoked) { |
|
5654 // Callbacks other than created callback must not be enqueued |
|
5655 // until after the created callback has been invoked. |
|
5656 return; |
|
5657 } |
|
5658 |
|
5659 // Add CALLBACK to ELEMENT's callback queue. |
|
5660 CustomElementCallback* callback = new CustomElementCallback(aCustomElement, |
|
5661 aType, |
|
5662 func, |
|
5663 elementData); |
|
5664 // Ownership of callback is taken by mCallbackQueue. |
|
5665 elementData->mCallbackQueue.AppendElement(callback); |
|
5666 if (aArgs) { |
|
5667 callback->SetArgs(*aArgs); |
|
5668 } |
|
5669 |
|
5670 if (!elementData->mElementIsBeingCreated) { |
|
5671 CustomElementData* lastData = |
|
5672 sProcessingStack.ref().SafeLastElement(nullptr); |
|
5673 |
|
5674 // A new element queue needs to be pushed if the queue at the |
|
5675 // top of the stack is associated with another microtask level. |
|
5676 // Don't push a queue for the level 0 microtask (base element queue) |
|
5677 // because we don't want to process the queue until the |
|
5678 // microtask checkpoint. |
|
5679 bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 && |
|
5680 (!lastData || lastData->mAssociatedMicroTask < |
|
5681 static_cast<int32_t>(nsContentUtils::MicroTaskLevel())); |
|
5682 |
|
5683 // Push a new element queue onto the processing stack when appropriate |
|
5684 // (when we enter a new microtask). |
|
5685 if (shouldPushElementQueue) { |
|
5686 // Push a sentinel value on the processing stack to mark the |
|
5687 // boundary between the element queues. |
|
5688 sProcessingStack.ref().AppendElement((CustomElementData*) nullptr); |
|
5689 } |
|
5690 |
|
5691 sProcessingStack.ref().AppendElement(elementData); |
|
5692 elementData->mAssociatedMicroTask = |
|
5693 static_cast<int32_t>(nsContentUtils::MicroTaskLevel()); |
|
5694 |
|
5695 // Add a script runner to pop and process the element queue at |
|
5696 // the top of the processing stack. |
|
5697 if (shouldPushElementQueue) { |
|
5698 // Lifecycle callbacks enqueued by user agent implementation |
|
5699 // should be invoked prior to returning control back to script. |
|
5700 // Create a script runner to process the top of the processing |
|
5701 // stack as soon as it is safe to run script. |
|
5702 nsContentUtils::AddScriptRunner(new ProcessStackRunner()); |
|
5703 } |
|
5704 } |
|
5705 } |
|
5706 |
|
5707 // static |
|
5708 void |
|
5709 nsDocument::ProcessBaseElementQueue() |
|
5710 { |
|
5711 // Prevent re-entrance. Also, if a microtask checkpoint is reached |
|
5712 // and there is no processing stack to process, then we are done. |
|
5713 if (sProcessingBaseElementQueue || sProcessingStack.empty()) { |
|
5714 return; |
|
5715 } |
|
5716 |
|
5717 MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0); |
|
5718 sProcessingBaseElementQueue = true; |
|
5719 nsContentUtils::AddScriptRunner(new ProcessStackRunner(true)); |
|
5720 } |
|
5721 |
|
5722 // static |
|
5723 void |
|
5724 nsDocument::ProcessTopElementQueue(bool aIsBaseQueue) |
|
5725 { |
|
5726 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); |
|
5727 |
|
5728 nsTArray<CustomElementData*>& stack = sProcessingStack.ref(); |
|
5729 uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr); |
|
5730 |
|
5731 if (aIsBaseQueue && firstQueue != 0) { |
|
5732 return; |
|
5733 } |
|
5734 |
|
5735 for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) { |
|
5736 // Callback queue may have already been processed in an earlier |
|
5737 // element queue or in an element queue that was popped |
|
5738 // off more recently. |
|
5739 if (stack[i]->mAssociatedMicroTask != -1) { |
|
5740 stack[i]->RunCallbackQueue(); |
|
5741 stack[i]->mAssociatedMicroTask = -1; |
|
5742 } |
|
5743 } |
|
5744 |
|
5745 // If this was actually the base element queue, don't bother trying to pop |
|
5746 // the first "queue" marker (sentinel). |
|
5747 if (firstQueue != 0) { |
|
5748 stack.SetLength(firstQueue); |
|
5749 } else { |
|
5750 // Don't pop sentinel for base element queue. |
|
5751 stack.SetLength(1); |
|
5752 sProcessingBaseElementQueue = false; |
|
5753 } |
|
5754 } |
|
5755 |
|
5756 bool |
|
5757 nsDocument::RegisterEnabled() |
|
5758 { |
|
5759 static bool sPrefValue = |
|
5760 Preferences::GetBool("dom.webcomponents.enabled", false); |
|
5761 return sPrefValue; |
|
5762 } |
|
5763 |
|
5764 // static |
|
5765 Maybe<nsTArray<mozilla::dom::CustomElementData*>> |
|
5766 nsDocument::sProcessingStack; |
|
5767 |
|
5768 // static |
|
5769 bool |
|
5770 nsDocument::sProcessingBaseElementQueue; |
|
5771 |
|
5772 void |
|
5773 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, |
|
5774 const ElementRegistrationOptions& aOptions, |
|
5775 JS::MutableHandle<JSObject*> aRetval, |
|
5776 ErrorResult& rv) |
|
5777 { |
|
5778 if (!mRegistry) { |
|
5779 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
5780 return; |
|
5781 } |
|
5782 |
|
5783 Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions; |
|
5784 |
|
5785 // Unconditionally convert TYPE to lowercase. |
|
5786 nsAutoString lcType; |
|
5787 nsContentUtils::ASCIIToLower(aType, lcType); |
|
5788 |
|
5789 // Only convert NAME to lowercase in HTML documents. Note that NAME is |
|
5790 // options.extends. |
|
5791 nsAutoString lcName; |
|
5792 if (IsHTML()) { |
|
5793 nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName); |
|
5794 } else { |
|
5795 lcName.Assign(aOptions.mExtends); |
|
5796 } |
|
5797 |
|
5798 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType)); |
|
5799 if (!nsContentUtils::IsCustomElementName(typeAtom)) { |
|
5800 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
|
5801 return; |
|
5802 } |
|
5803 |
|
5804 // If there already exists a definition with the same TYPE, set ERROR to |
|
5805 // DuplicateDefinition and stop. |
|
5806 // Note that we need to find existing custom elements from either namespace. |
|
5807 CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom); |
|
5808 if (definitions.Get(&duplicateFinder)) { |
|
5809 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
5810 return; |
|
5811 } |
|
5812 |
|
5813 nsIGlobalObject* sgo = GetScopeObject(); |
|
5814 if (!sgo) { |
|
5815 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5816 return; |
|
5817 } |
|
5818 JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject()); |
|
5819 |
|
5820 JSAutoCompartment ac(aCx, global); |
|
5821 |
|
5822 JS::Handle<JSObject*> htmlProto( |
|
5823 HTMLElementBinding::GetProtoObject(aCx, global)); |
|
5824 if (!htmlProto) { |
|
5825 rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
5826 return; |
|
5827 } |
|
5828 |
|
5829 int32_t namespaceID = kNameSpaceID_XHTML; |
|
5830 JS::Rooted<JSObject*> protoObject(aCx); |
|
5831 if (!aOptions.mPrototype) { |
|
5832 protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr()); |
|
5833 if (!protoObject) { |
|
5834 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5835 return; |
|
5836 } |
|
5837 } else { |
|
5838 // If a prototype is provided, we must check to ensure that it is from the |
|
5839 // same browsing context as us. |
|
5840 protoObject = aOptions.mPrototype; |
|
5841 if (JS_GetGlobalForObject(aCx, protoObject) != global) { |
|
5842 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
5843 return; |
|
5844 } |
|
5845 |
|
5846 // If PROTOTYPE is already an interface prototype object for any interface |
|
5847 // object or PROTOTYPE has a non-configurable property named constructor, |
|
5848 // throw a NotSupportedError and stop. |
|
5849 const js::Class* clasp = js::GetObjectClass(protoObject); |
|
5850 if (IsDOMIfaceAndProtoClass(clasp)) { |
|
5851 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
5852 return; |
|
5853 } |
|
5854 |
|
5855 JS::Rooted<JSPropertyDescriptor> descRoot(aCx); |
|
5856 JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot); |
|
5857 if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) { |
|
5858 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5859 return; |
|
5860 } |
|
5861 |
|
5862 // Check if non-configurable |
|
5863 if (desc.isPermanent()) { |
|
5864 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
5865 return; |
|
5866 } |
|
5867 |
|
5868 JS::Handle<JSObject*> svgProto( |
|
5869 SVGElementBinding::GetProtoObject(aCx, global)); |
|
5870 if (!svgProto) { |
|
5871 rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
5872 return; |
|
5873 } |
|
5874 |
|
5875 JS::Rooted<JSObject*> protoProto(aCx, protoObject); |
|
5876 |
|
5877 // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG |
|
5878 // Namespace. |
|
5879 while (protoProto) { |
|
5880 if (protoProto == htmlProto) { |
|
5881 break; |
|
5882 } |
|
5883 |
|
5884 if (protoProto == svgProto) { |
|
5885 namespaceID = kNameSpaceID_SVG; |
|
5886 break; |
|
5887 } |
|
5888 |
|
5889 if (!JS_GetPrototype(aCx, protoProto, &protoProto)) { |
|
5890 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5891 return; |
|
5892 } |
|
5893 } |
|
5894 } |
|
5895 |
|
5896 // If name was provided and not null... |
|
5897 nsCOMPtr<nsIAtom> nameAtom; |
|
5898 if (!lcName.IsEmpty()) { |
|
5899 // Let BASE be the element interface for NAME and NAMESPACE. |
|
5900 bool known = false; |
|
5901 nameAtom = do_GetAtom(lcName); |
|
5902 if (namespaceID == kNameSpaceID_XHTML) { |
|
5903 nsIParserService* ps = nsContentUtils::GetParserService(); |
|
5904 if (!ps) { |
|
5905 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5906 return; |
|
5907 } |
|
5908 |
|
5909 known = |
|
5910 ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined; |
|
5911 } else { |
|
5912 known = SVGElementFactory::Exists(nameAtom); |
|
5913 } |
|
5914 |
|
5915 // If BASE does not exist or is an interface for a custom element, set ERROR |
|
5916 // to InvalidName and stop. |
|
5917 // If BASE exists, then it cannot be an interface for a custom element. |
|
5918 if (!known) { |
|
5919 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
|
5920 return; |
|
5921 } |
|
5922 } else { |
|
5923 // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop. |
|
5924 if (namespaceID == kNameSpaceID_SVG) { |
|
5925 rv.Throw(NS_ERROR_UNEXPECTED); |
|
5926 return; |
|
5927 } |
|
5928 |
|
5929 nameAtom = typeAtom; |
|
5930 } |
|
5931 |
|
5932 nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks()); |
|
5933 JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject)); |
|
5934 if (!callbacksHolder->Init(aCx, rootedv)) { |
|
5935 rv.Throw(NS_ERROR_FAILURE); |
|
5936 return; |
|
5937 } |
|
5938 |
|
5939 // Associate the definition with the custom element. |
|
5940 CustomElementHashKey key(namespaceID, typeAtom); |
|
5941 LifecycleCallbacks* callbacks = callbacksHolder.forget(); |
|
5942 CustomElementDefinition* definition = |
|
5943 new CustomElementDefinition(protoObject, |
|
5944 typeAtom, |
|
5945 nameAtom, |
|
5946 callbacks, |
|
5947 namespaceID, |
|
5948 0 /* TODO dependent on HTML imports. Bug 877072 */); |
|
5949 definitions.Put(&key, definition); |
|
5950 |
|
5951 // Do element upgrade. |
|
5952 nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates; |
|
5953 mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates); |
|
5954 if (candidates) { |
|
5955 for (size_t i = 0; i < candidates->Length(); ++i) { |
|
5956 Element *elem = candidates->ElementAt(i); |
|
5957 |
|
5958 // Make sure that the element name matches the name in the definition. |
|
5959 // (e.g. a definition for x-button extending button should match |
|
5960 // <button is="x-button"> but not <x-button>. |
|
5961 if (elem->NodeInfo()->NameAtom() != nameAtom) { |
|
5962 // Skip over this element because definition does not apply. |
|
5963 continue; |
|
5964 } |
|
5965 |
|
5966 nsWrapperCache* cache; |
|
5967 CallQueryInterface(elem, &cache); |
|
5968 MOZ_ASSERT(cache, "Element doesn't support wrapper cache?"); |
|
5969 |
|
5970 JS::RootedObject wrapper(aCx); |
|
5971 if ((wrapper = cache->GetWrapper())) { |
|
5972 if (!JS_SetPrototype(aCx, wrapper, protoObject)) { |
|
5973 continue; |
|
5974 } |
|
5975 } |
|
5976 |
|
5977 EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition); |
|
5978 if (elem->GetCurrentDoc()) { |
|
5979 // Normally callbacks can not be enqueued until the created |
|
5980 // callback has been invoked, however, the attached callback |
|
5981 // in element upgrade is an exception so pretend the created |
|
5982 // callback has been invoked. |
|
5983 elem->GetCustomElementData()->mCreatedCallbackInvoked = true; |
|
5984 |
|
5985 EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition); |
|
5986 } |
|
5987 } |
|
5988 } |
|
5989 |
|
5990 // Create constructor to return. Store the name of the custom element as the |
|
5991 // name of the function. |
|
5992 JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0, |
|
5993 JSFUN_CONSTRUCTOR, JS::NullPtr(), |
|
5994 NS_ConvertUTF16toUTF8(lcType).get()); |
|
5995 if (!constructor) { |
|
5996 rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
5997 return; |
|
5998 } |
|
5999 |
|
6000 aRetval.set(JS_GetFunctionObject(constructor)); |
|
6001 } |
|
6002 |
|
6003 void |
|
6004 nsDocument::UseRegistryFromDocument(nsIDocument* aDocument) |
|
6005 { |
|
6006 nsDocument* doc = static_cast<nsDocument*>(aDocument); |
|
6007 MOZ_ASSERT(!mRegistry, "There should be no existing registry."); |
|
6008 mRegistry = doc->mRegistry; |
|
6009 } |
|
6010 |
|
6011 NS_IMETHODIMP |
|
6012 nsDocument::GetElementsByTagName(const nsAString& aTagname, |
|
6013 nsIDOMNodeList** aReturn) |
|
6014 { |
|
6015 nsRefPtr<nsContentList> list = GetElementsByTagName(aTagname); |
|
6016 NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); |
|
6017 |
|
6018 // transfer ref to aReturn |
|
6019 list.forget(aReturn); |
|
6020 return NS_OK; |
|
6021 } |
|
6022 |
|
6023 already_AddRefed<nsContentList> |
|
6024 nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, |
|
6025 const nsAString& aLocalName, |
|
6026 ErrorResult& aResult) |
|
6027 { |
|
6028 int32_t nameSpaceId = kNameSpaceID_Wildcard; |
|
6029 |
|
6030 if (!aNamespaceURI.EqualsLiteral("*")) { |
|
6031 aResult = |
|
6032 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
|
6033 nameSpaceId); |
|
6034 if (aResult.Failed()) { |
|
6035 return nullptr; |
|
6036 } |
|
6037 } |
|
6038 |
|
6039 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); |
|
6040 |
|
6041 return NS_GetContentList(this, nameSpaceId, aLocalName); |
|
6042 } |
|
6043 |
|
6044 NS_IMETHODIMP |
|
6045 nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, |
|
6046 const nsAString& aLocalName, |
|
6047 nsIDOMNodeList** aReturn) |
|
6048 { |
|
6049 ErrorResult rv; |
|
6050 nsRefPtr<nsContentList> list = |
|
6051 nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv); |
|
6052 if (rv.Failed()) { |
|
6053 return rv.ErrorCode(); |
|
6054 } |
|
6055 |
|
6056 // transfer ref to aReturn |
|
6057 list.forget(aReturn); |
|
6058 return NS_OK; |
|
6059 } |
|
6060 |
|
6061 NS_IMETHODIMP |
|
6062 nsDocument::GetAsync(bool *aAsync) |
|
6063 { |
|
6064 NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!"); |
|
6065 |
|
6066 return NS_ERROR_NOT_IMPLEMENTED; |
|
6067 } |
|
6068 |
|
6069 NS_IMETHODIMP |
|
6070 nsDocument::SetAsync(bool aAsync) |
|
6071 { |
|
6072 NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!"); |
|
6073 |
|
6074 return NS_ERROR_NOT_IMPLEMENTED; |
|
6075 } |
|
6076 |
|
6077 NS_IMETHODIMP |
|
6078 nsDocument::Load(const nsAString& aUrl, bool *aReturn) |
|
6079 { |
|
6080 NS_ERROR("nsDocument::Load() should be overriden by subclass!"); |
|
6081 |
|
6082 return NS_ERROR_NOT_IMPLEMENTED; |
|
6083 } |
|
6084 |
|
6085 NS_IMETHODIMP |
|
6086 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets) |
|
6087 { |
|
6088 NS_ADDREF(*aStyleSheets = StyleSheets()); |
|
6089 return NS_OK; |
|
6090 } |
|
6091 |
|
6092 StyleSheetList* |
|
6093 nsDocument::StyleSheets() |
|
6094 { |
|
6095 if (!mDOMStyleSheets) { |
|
6096 mDOMStyleSheets = new nsDOMStyleSheetList(this); |
|
6097 } |
|
6098 return mDOMStyleSheets; |
|
6099 } |
|
6100 |
|
6101 NS_IMETHODIMP |
|
6102 nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet) |
|
6103 { |
|
6104 nsIDocument::GetSelectedStyleSheetSet(aSheetSet); |
|
6105 return NS_OK; |
|
6106 } |
|
6107 |
|
6108 void |
|
6109 nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet) |
|
6110 { |
|
6111 aSheetSet.Truncate(); |
|
6112 |
|
6113 // Look through our sheets, find the selected set title |
|
6114 int32_t count = GetNumberOfStyleSheets(); |
|
6115 nsAutoString title; |
|
6116 for (int32_t index = 0; index < count; index++) { |
|
6117 nsIStyleSheet* sheet = GetStyleSheetAt(index); |
|
6118 NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
|
6119 |
|
6120 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet); |
|
6121 NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet"); |
|
6122 bool disabled; |
|
6123 domSheet->GetDisabled(&disabled); |
|
6124 if (disabled) { |
|
6125 // Disabled sheets don't affect the currently selected set |
|
6126 continue; |
|
6127 } |
|
6128 |
|
6129 sheet->GetTitle(title); |
|
6130 |
|
6131 if (aSheetSet.IsEmpty()) { |
|
6132 aSheetSet = title; |
|
6133 } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) { |
|
6134 // Sheets from multiple sets enabled; return null string, per spec. |
|
6135 SetDOMStringToNull(aSheetSet); |
|
6136 return; |
|
6137 } |
|
6138 } |
|
6139 } |
|
6140 |
|
6141 NS_IMETHODIMP |
|
6142 nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet) |
|
6143 { |
|
6144 SetSelectedStyleSheetSet(aSheetSet); |
|
6145 return NS_OK; |
|
6146 } |
|
6147 |
|
6148 void |
|
6149 nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet) |
|
6150 { |
|
6151 if (DOMStringIsNull(aSheetSet)) { |
|
6152 return; |
|
6153 } |
|
6154 |
|
6155 // Must update mLastStyleSheetSet before doing anything else with stylesheets |
|
6156 // or CSSLoaders. |
|
6157 mLastStyleSheetSet = aSheetSet; |
|
6158 EnableStyleSheetsForSetInternal(aSheetSet, true); |
|
6159 } |
|
6160 |
|
6161 NS_IMETHODIMP |
|
6162 nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet) |
|
6163 { |
|
6164 nsString sheetSet; |
|
6165 GetLastStyleSheetSet(sheetSet); |
|
6166 aSheetSet = sheetSet; |
|
6167 return NS_OK; |
|
6168 } |
|
6169 |
|
6170 void |
|
6171 nsDocument::GetLastStyleSheetSet(nsString& aSheetSet) |
|
6172 { |
|
6173 aSheetSet = mLastStyleSheetSet; |
|
6174 } |
|
6175 |
|
6176 NS_IMETHODIMP |
|
6177 nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet) |
|
6178 { |
|
6179 nsIDocument::GetPreferredStyleSheetSet(aSheetSet); |
|
6180 return NS_OK; |
|
6181 } |
|
6182 |
|
6183 void |
|
6184 nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet) |
|
6185 { |
|
6186 GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet); |
|
6187 } |
|
6188 |
|
6189 NS_IMETHODIMP |
|
6190 nsDocument::GetStyleSheetSets(nsISupports** aList) |
|
6191 { |
|
6192 NS_ADDREF(*aList = StyleSheetSets()); |
|
6193 return NS_OK; |
|
6194 } |
|
6195 |
|
6196 DOMStringList* |
|
6197 nsDocument::StyleSheetSets() |
|
6198 { |
|
6199 if (!mStyleSheetSetList) { |
|
6200 mStyleSheetSetList = new nsDOMStyleSheetSetList(this); |
|
6201 } |
|
6202 return mStyleSheetSetList; |
|
6203 } |
|
6204 |
|
6205 NS_IMETHODIMP |
|
6206 nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet) |
|
6207 { |
|
6208 EnableStyleSheetsForSet(aSheetSet); |
|
6209 return NS_OK; |
|
6210 } |
|
6211 |
|
6212 void |
|
6213 nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet) |
|
6214 { |
|
6215 // Per spec, passing in null is a no-op. |
|
6216 if (!DOMStringIsNull(aSheetSet)) { |
|
6217 // Note: must make sure to not change the CSSLoader's preferred sheet -- |
|
6218 // that value should be equal to either our lastStyleSheetSet (if that's |
|
6219 // non-null) or to our preferredStyleSheetSet. And this method doesn't |
|
6220 // change either of those. |
|
6221 EnableStyleSheetsForSetInternal(aSheetSet, false); |
|
6222 } |
|
6223 } |
|
6224 |
|
6225 void |
|
6226 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet, |
|
6227 bool aUpdateCSSLoader) |
|
6228 { |
|
6229 BeginUpdate(UPDATE_STYLE); |
|
6230 int32_t count = GetNumberOfStyleSheets(); |
|
6231 nsAutoString title; |
|
6232 for (int32_t index = 0; index < count; index++) { |
|
6233 nsIStyleSheet* sheet = GetStyleSheetAt(index); |
|
6234 NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
|
6235 sheet->GetTitle(title); |
|
6236 if (!title.IsEmpty()) { |
|
6237 sheet->SetEnabled(title.Equals(aSheetSet)); |
|
6238 } |
|
6239 } |
|
6240 if (aUpdateCSSLoader) { |
|
6241 CSSLoader()->SetPreferredSheet(aSheetSet); |
|
6242 } |
|
6243 EndUpdate(UPDATE_STYLE); |
|
6244 } |
|
6245 |
|
6246 NS_IMETHODIMP |
|
6247 nsDocument::GetCharacterSet(nsAString& aCharacterSet) |
|
6248 { |
|
6249 nsIDocument::GetCharacterSet(aCharacterSet); |
|
6250 return NS_OK; |
|
6251 } |
|
6252 |
|
6253 void |
|
6254 nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const |
|
6255 { |
|
6256 CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet); |
|
6257 } |
|
6258 |
|
6259 NS_IMETHODIMP |
|
6260 nsDocument::ImportNode(nsIDOMNode* aImportedNode, |
|
6261 bool aDeep, |
|
6262 uint8_t aArgc, |
|
6263 nsIDOMNode** aResult) |
|
6264 { |
|
6265 if (aArgc == 0) { |
|
6266 aDeep = true; |
|
6267 } |
|
6268 |
|
6269 *aResult = nullptr; |
|
6270 |
|
6271 nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode); |
|
6272 NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED); |
|
6273 |
|
6274 ErrorResult rv; |
|
6275 nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv); |
|
6276 if (rv.Failed()) { |
|
6277 return rv.ErrorCode(); |
|
6278 } |
|
6279 |
|
6280 NS_ADDREF(*aResult = result->AsDOMNode()); |
|
6281 return NS_OK; |
|
6282 } |
|
6283 |
|
6284 already_AddRefed<nsINode> |
|
6285 nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const |
|
6286 { |
|
6287 nsINode* imported = &aNode; |
|
6288 |
|
6289 switch (imported->NodeType()) { |
|
6290 case nsIDOMNode::ATTRIBUTE_NODE: |
|
6291 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: |
|
6292 case nsIDOMNode::ELEMENT_NODE: |
|
6293 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: |
|
6294 case nsIDOMNode::TEXT_NODE: |
|
6295 case nsIDOMNode::CDATA_SECTION_NODE: |
|
6296 case nsIDOMNode::COMMENT_NODE: |
|
6297 case nsIDOMNode::DOCUMENT_TYPE_NODE: |
|
6298 { |
|
6299 nsCOMPtr<nsINode> newNode; |
|
6300 nsCOMArray<nsINode> nodesWithProperties; |
|
6301 rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, |
|
6302 nodesWithProperties, getter_AddRefs(newNode)); |
|
6303 if (rv.Failed()) { |
|
6304 return nullptr; |
|
6305 } |
|
6306 |
|
6307 nsIDocument *ownerDoc = imported->OwnerDoc(); |
|
6308 rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, ownerDoc, |
|
6309 nsIDOMUserDataHandler::NODE_IMPORTED, |
|
6310 true); |
|
6311 if (rv.Failed()) { |
|
6312 return nullptr; |
|
6313 } |
|
6314 |
|
6315 return newNode.forget(); |
|
6316 } |
|
6317 default: |
|
6318 { |
|
6319 NS_WARNING("Don't know how to clone this nodetype for importNode."); |
|
6320 |
|
6321 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
6322 } |
|
6323 } |
|
6324 |
|
6325 return nullptr; |
|
6326 } |
|
6327 |
|
6328 NS_IMETHODIMP |
|
6329 nsDocument::LoadBindingDocument(const nsAString& aURI) |
|
6330 { |
|
6331 ErrorResult rv; |
|
6332 nsIDocument::LoadBindingDocument(aURI, rv); |
|
6333 return rv.ErrorCode(); |
|
6334 } |
|
6335 |
|
6336 void |
|
6337 nsIDocument::LoadBindingDocument(const nsAString& aURI, ErrorResult& rv) |
|
6338 { |
|
6339 nsCOMPtr<nsIURI> uri; |
|
6340 rv = NS_NewURI(getter_AddRefs(uri), aURI, |
|
6341 mCharacterSet.get(), |
|
6342 GetDocBaseURI()); |
|
6343 if (rv.Failed()) { |
|
6344 return; |
|
6345 } |
|
6346 |
|
6347 // Figure out the right principal to use |
|
6348 nsCOMPtr<nsIPrincipal> subject; |
|
6349 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
|
6350 if (secMan) { |
|
6351 rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject)); |
|
6352 if (rv.Failed()) { |
|
6353 return; |
|
6354 } |
|
6355 } |
|
6356 |
|
6357 if (!subject) { |
|
6358 // Fall back to our principal. Or should we fall back to the null |
|
6359 // principal? The latter would just mean no binding loads.... |
|
6360 subject = NodePrincipal(); |
|
6361 } |
|
6362 |
|
6363 BindingManager()->LoadBindingDocument(this, uri, subject); |
|
6364 } |
|
6365 |
|
6366 NS_IMETHODIMP |
|
6367 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult) |
|
6368 { |
|
6369 nsCOMPtr<nsINode> node = do_QueryInterface(aNode); |
|
6370 NS_ENSURE_ARG_POINTER(node); |
|
6371 |
|
6372 Element* bindingParent = nsIDocument::GetBindingParent(*node); |
|
6373 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent); |
|
6374 retval.forget(aResult); |
|
6375 return NS_OK; |
|
6376 } |
|
6377 |
|
6378 Element* |
|
6379 nsIDocument::GetBindingParent(nsINode& aNode) |
|
6380 { |
|
6381 nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode)); |
|
6382 if (!content) |
|
6383 return nullptr; |
|
6384 |
|
6385 nsIContent* bindingParent = content->GetBindingParent(); |
|
6386 return bindingParent ? bindingParent->AsElement() : nullptr; |
|
6387 } |
|
6388 |
|
6389 static Element* |
|
6390 GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName, |
|
6391 const nsAString& aAttrValue, bool aUniversalMatch) |
|
6392 { |
|
6393 if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) : |
|
6394 aContent->AttrValueIs(kNameSpaceID_None, aAttrName, |
|
6395 aAttrValue, eCaseMatters)) { |
|
6396 return aContent->AsElement(); |
|
6397 } |
|
6398 |
|
6399 for (nsIContent* child = aContent->GetFirstChild(); |
|
6400 child; |
|
6401 child = child->GetNextSibling()) { |
|
6402 |
|
6403 Element* matchedElement = |
|
6404 GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch); |
|
6405 if (matchedElement) |
|
6406 return matchedElement; |
|
6407 } |
|
6408 |
|
6409 return nullptr; |
|
6410 } |
|
6411 |
|
6412 Element* |
|
6413 nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement, |
|
6414 nsIAtom* aAttrName, |
|
6415 const nsAString& aAttrValue) const |
|
6416 { |
|
6417 nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement); |
|
6418 if (!nodeList) |
|
6419 return nullptr; |
|
6420 |
|
6421 uint32_t length = 0; |
|
6422 nodeList->GetLength(&length); |
|
6423 |
|
6424 bool universalMatch = aAttrValue.EqualsLiteral("*"); |
|
6425 |
|
6426 for (uint32_t i = 0; i < length; ++i) { |
|
6427 nsIContent* current = nodeList->Item(i); |
|
6428 Element* matchedElm = |
|
6429 GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch); |
|
6430 if (matchedElm) |
|
6431 return matchedElm; |
|
6432 } |
|
6433 |
|
6434 return nullptr; |
|
6435 } |
|
6436 |
|
6437 NS_IMETHODIMP |
|
6438 nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement, |
|
6439 const nsAString& aAttrName, |
|
6440 const nsAString& aAttrValue, |
|
6441 nsIDOMElement** aResult) |
|
6442 { |
|
6443 nsCOMPtr<Element> element = do_QueryInterface(aElement); |
|
6444 NS_ENSURE_ARG_POINTER(element); |
|
6445 |
|
6446 Element* anonEl = |
|
6447 nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName, |
|
6448 aAttrValue); |
|
6449 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl); |
|
6450 retval.forget(aResult); |
|
6451 return NS_OK; |
|
6452 } |
|
6453 |
|
6454 Element* |
|
6455 nsIDocument::GetAnonymousElementByAttribute(Element& aElement, |
|
6456 const nsAString& aAttrName, |
|
6457 const nsAString& aAttrValue) |
|
6458 { |
|
6459 nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName); |
|
6460 |
|
6461 return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue); |
|
6462 } |
|
6463 |
|
6464 |
|
6465 NS_IMETHODIMP |
|
6466 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement, |
|
6467 nsIDOMNodeList** aResult) |
|
6468 { |
|
6469 *aResult = nullptr; |
|
6470 |
|
6471 nsCOMPtr<nsIContent> content(do_QueryInterface(aElement)); |
|
6472 return BindingManager()->GetAnonymousNodesFor(content, aResult); |
|
6473 } |
|
6474 |
|
6475 nsINodeList* |
|
6476 nsIDocument::GetAnonymousNodes(Element& aElement) |
|
6477 { |
|
6478 return BindingManager()->GetAnonymousNodesFor(&aElement); |
|
6479 } |
|
6480 |
|
6481 NS_IMETHODIMP |
|
6482 nsDocument::CreateRange(nsIDOMRange** aReturn) |
|
6483 { |
|
6484 ErrorResult rv; |
|
6485 *aReturn = nsIDocument::CreateRange(rv).take(); |
|
6486 return rv.ErrorCode(); |
|
6487 } |
|
6488 |
|
6489 already_AddRefed<nsRange> |
|
6490 nsIDocument::CreateRange(ErrorResult& rv) |
|
6491 { |
|
6492 nsRefPtr<nsRange> range = new nsRange(this); |
|
6493 nsresult res = range->Set(this, 0, this, 0); |
|
6494 if (NS_FAILED(res)) { |
|
6495 rv.Throw(res); |
|
6496 return nullptr; |
|
6497 } |
|
6498 |
|
6499 return range.forget(); |
|
6500 } |
|
6501 |
|
6502 NS_IMETHODIMP |
|
6503 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot, |
|
6504 uint32_t aWhatToShow, |
|
6505 nsIDOMNodeFilter *aFilter, |
|
6506 uint8_t aOptionalArgc, |
|
6507 nsIDOMNodeIterator **_retval) |
|
6508 { |
|
6509 *_retval = nullptr; |
|
6510 |
|
6511 if (!aOptionalArgc) { |
|
6512 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL; |
|
6513 } |
|
6514 |
|
6515 if (!aRoot) { |
|
6516 return NS_ERROR_DOM_NOT_SUPPORTED_ERR; |
|
6517 } |
|
6518 |
|
6519 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot); |
|
6520 NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED); |
|
6521 |
|
6522 ErrorResult rv; |
|
6523 NodeFilterHolder holder(aFilter); |
|
6524 *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow, holder, |
|
6525 rv).take(); |
|
6526 return rv.ErrorCode(); |
|
6527 } |
|
6528 |
|
6529 already_AddRefed<NodeIterator> |
|
6530 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow, |
|
6531 NodeFilter* aFilter, |
|
6532 ErrorResult& rv) const |
|
6533 { |
|
6534 NodeFilterHolder holder(aFilter); |
|
6535 return CreateNodeIterator(aRoot, aWhatToShow, holder, rv); |
|
6536 } |
|
6537 |
|
6538 already_AddRefed<NodeIterator> |
|
6539 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow, |
|
6540 const NodeFilterHolder& aFilter, |
|
6541 ErrorResult& rv) const |
|
6542 { |
|
6543 nsINode* root = &aRoot; |
|
6544 nsRefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow, |
|
6545 aFilter); |
|
6546 return iterator.forget(); |
|
6547 } |
|
6548 |
|
6549 NS_IMETHODIMP |
|
6550 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot, |
|
6551 uint32_t aWhatToShow, |
|
6552 nsIDOMNodeFilter *aFilter, |
|
6553 uint8_t aOptionalArgc, |
|
6554 nsIDOMTreeWalker **_retval) |
|
6555 { |
|
6556 *_retval = nullptr; |
|
6557 |
|
6558 if (!aOptionalArgc) { |
|
6559 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL; |
|
6560 } |
|
6561 |
|
6562 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot); |
|
6563 NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
6564 |
|
6565 ErrorResult rv; |
|
6566 NodeFilterHolder holder(aFilter); |
|
6567 *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow, holder, |
|
6568 rv).take(); |
|
6569 return rv.ErrorCode(); |
|
6570 } |
|
6571 |
|
6572 already_AddRefed<TreeWalker> |
|
6573 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow, |
|
6574 NodeFilter* aFilter, |
|
6575 ErrorResult& rv) const |
|
6576 { |
|
6577 NodeFilterHolder holder(aFilter); |
|
6578 return CreateTreeWalker(aRoot, aWhatToShow, holder, rv); |
|
6579 } |
|
6580 |
|
6581 already_AddRefed<TreeWalker> |
|
6582 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow, |
|
6583 const NodeFilterHolder& aFilter, |
|
6584 ErrorResult& rv) const |
|
6585 { |
|
6586 nsINode* root = &aRoot; |
|
6587 nsRefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, aFilter); |
|
6588 return walker.forget(); |
|
6589 } |
|
6590 |
|
6591 |
|
6592 NS_IMETHODIMP |
|
6593 nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView) |
|
6594 { |
|
6595 *aDefaultView = nullptr; |
|
6596 nsCOMPtr<nsPIDOMWindow> win = GetWindow(); |
|
6597 win.forget(aDefaultView); |
|
6598 return NS_OK; |
|
6599 } |
|
6600 |
|
6601 NS_IMETHODIMP |
|
6602 nsDocument::GetLocation(nsIDOMLocation **_retval) |
|
6603 { |
|
6604 *_retval = nsIDocument::GetLocation().take(); |
|
6605 return NS_OK; |
|
6606 } |
|
6607 |
|
6608 already_AddRefed<nsIDOMLocation> |
|
6609 nsIDocument::GetLocation() const |
|
6610 { |
|
6611 nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject); |
|
6612 |
|
6613 if (!w) { |
|
6614 return nullptr; |
|
6615 } |
|
6616 |
|
6617 nsCOMPtr<nsIDOMLocation> loc; |
|
6618 w->GetLocation(getter_AddRefs(loc)); |
|
6619 return loc.forget(); |
|
6620 } |
|
6621 |
|
6622 Element* |
|
6623 nsIDocument::GetHtmlElement() const |
|
6624 { |
|
6625 Element* rootElement = GetRootElement(); |
|
6626 if (rootElement && rootElement->IsHTML(nsGkAtoms::html)) |
|
6627 return rootElement; |
|
6628 return nullptr; |
|
6629 } |
|
6630 |
|
6631 Element* |
|
6632 nsIDocument::GetHtmlChildElement(nsIAtom* aTag) |
|
6633 { |
|
6634 Element* html = GetHtmlElement(); |
|
6635 if (!html) |
|
6636 return nullptr; |
|
6637 |
|
6638 // Look for the element with aTag inside html. This needs to run |
|
6639 // forwards to find the first such element. |
|
6640 for (nsIContent* child = html->GetFirstChild(); |
|
6641 child; |
|
6642 child = child->GetNextSibling()) { |
|
6643 if (child->IsHTML(aTag)) |
|
6644 return child->AsElement(); |
|
6645 } |
|
6646 return nullptr; |
|
6647 } |
|
6648 |
|
6649 nsIContent* |
|
6650 nsDocument::GetTitleContent(uint32_t aNamespace) |
|
6651 { |
|
6652 // mMayHaveTitleElement will have been set to true if any HTML or SVG |
|
6653 // <title> element has been bound to this document. So if it's false, |
|
6654 // we know there is nothing to do here. This avoids us having to search |
|
6655 // the whole DOM if someone calls document.title on a large document |
|
6656 // without a title. |
|
6657 if (!mMayHaveTitleElement) |
|
6658 return nullptr; |
|
6659 |
|
6660 nsRefPtr<nsContentList> list = |
|
6661 NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title")); |
|
6662 |
|
6663 return list->Item(0, false); |
|
6664 } |
|
6665 |
|
6666 void |
|
6667 nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle) |
|
6668 { |
|
6669 nsIContent* title = GetTitleContent(aNamespace); |
|
6670 if (!title) |
|
6671 return; |
|
6672 if(!nsContentUtils::GetNodeTextContent(title, false, aTitle)) |
|
6673 NS_RUNTIMEABORT("OOM"); |
|
6674 } |
|
6675 |
|
6676 NS_IMETHODIMP |
|
6677 nsDocument::GetTitle(nsAString& aTitle) |
|
6678 { |
|
6679 nsString title; |
|
6680 GetTitle(title); |
|
6681 aTitle = title; |
|
6682 return NS_OK; |
|
6683 } |
|
6684 |
|
6685 void |
|
6686 nsDocument::GetTitle(nsString& aTitle) |
|
6687 { |
|
6688 aTitle.Truncate(); |
|
6689 |
|
6690 nsIContent *rootElement = GetRootElement(); |
|
6691 if (!rootElement) |
|
6692 return; |
|
6693 |
|
6694 nsAutoString tmp; |
|
6695 |
|
6696 switch (rootElement->GetNameSpaceID()) { |
|
6697 #ifdef MOZ_XUL |
|
6698 case kNameSpaceID_XUL: |
|
6699 rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp); |
|
6700 break; |
|
6701 #endif |
|
6702 case kNameSpaceID_SVG: |
|
6703 if (rootElement->Tag() == nsGkAtoms::svg) { |
|
6704 GetTitleFromElement(kNameSpaceID_SVG, tmp); |
|
6705 break; |
|
6706 } // else fall through |
|
6707 default: |
|
6708 GetTitleFromElement(kNameSpaceID_XHTML, tmp); |
|
6709 break; |
|
6710 } |
|
6711 |
|
6712 tmp.CompressWhitespace(); |
|
6713 aTitle = tmp; |
|
6714 } |
|
6715 |
|
6716 NS_IMETHODIMP |
|
6717 nsDocument::SetTitle(const nsAString& aTitle) |
|
6718 { |
|
6719 Element *rootElement = GetRootElement(); |
|
6720 if (!rootElement) |
|
6721 return NS_OK; |
|
6722 |
|
6723 switch (rootElement->GetNameSpaceID()) { |
|
6724 case kNameSpaceID_SVG: |
|
6725 return NS_OK; // SVG doesn't support setting a title |
|
6726 #ifdef MOZ_XUL |
|
6727 case kNameSpaceID_XUL: |
|
6728 return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title, |
|
6729 aTitle, true); |
|
6730 #endif |
|
6731 } |
|
6732 |
|
6733 // Batch updates so that mutation events don't change "the title |
|
6734 // element" under us |
|
6735 mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true); |
|
6736 |
|
6737 nsIContent* title = GetTitleContent(kNameSpaceID_XHTML); |
|
6738 if (!title) { |
|
6739 Element *head = GetHeadElement(); |
|
6740 if (!head) |
|
6741 return NS_OK; |
|
6742 |
|
6743 { |
|
6744 nsCOMPtr<nsINodeInfo> titleInfo; |
|
6745 titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr, |
|
6746 kNameSpaceID_XHTML, |
|
6747 nsIDOMNode::ELEMENT_NODE); |
|
6748 title = NS_NewHTMLTitleElement(titleInfo.forget()); |
|
6749 if (!title) |
|
6750 return NS_OK; |
|
6751 } |
|
6752 |
|
6753 head->AppendChildTo(title, true); |
|
6754 } |
|
6755 |
|
6756 return nsContentUtils::SetNodeTextContent(title, aTitle, false); |
|
6757 } |
|
6758 |
|
6759 void |
|
6760 nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv) |
|
6761 { |
|
6762 rv = SetTitle(aTitle); |
|
6763 } |
|
6764 |
|
6765 void |
|
6766 nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement) |
|
6767 { |
|
6768 NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement, |
|
6769 "Setting a title while unlinking or destroying the element?"); |
|
6770 if (mInUnlinkOrDeletion) { |
|
6771 return; |
|
6772 } |
|
6773 |
|
6774 if (aBoundTitleElement) { |
|
6775 mMayHaveTitleElement = true; |
|
6776 } |
|
6777 if (mPendingTitleChangeEvent.IsPending()) |
|
6778 return; |
|
6779 |
|
6780 nsRefPtr<nsRunnableMethod<nsDocument, void, false> > event = |
|
6781 NS_NewNonOwningRunnableMethod(this, |
|
6782 &nsDocument::DoNotifyPossibleTitleChange); |
|
6783 nsresult rv = NS_DispatchToCurrentThread(event); |
|
6784 if (NS_SUCCEEDED(rv)) { |
|
6785 mPendingTitleChangeEvent = event; |
|
6786 } |
|
6787 } |
|
6788 |
|
6789 void |
|
6790 nsDocument::DoNotifyPossibleTitleChange() |
|
6791 { |
|
6792 mPendingTitleChangeEvent.Forget(); |
|
6793 mHaveFiredTitleChange = true; |
|
6794 |
|
6795 nsAutoString title; |
|
6796 GetTitle(title); |
|
6797 |
|
6798 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
6799 if (shell) { |
|
6800 nsCOMPtr<nsISupports> container = |
|
6801 shell->GetPresContext()->GetContainerWeak(); |
|
6802 if (container) { |
|
6803 nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container); |
|
6804 if (docShellWin) { |
|
6805 docShellWin->SetTitle(title.get()); |
|
6806 } |
|
6807 } |
|
6808 } |
|
6809 |
|
6810 // Fire a DOM event for the title change. |
|
6811 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this), |
|
6812 NS_LITERAL_STRING("DOMTitleChanged"), |
|
6813 true, true); |
|
6814 } |
|
6815 |
|
6816 already_AddRefed<nsIBoxObject> |
|
6817 nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv) |
|
6818 { |
|
6819 if (!aElement) { |
|
6820 aRv.Throw(NS_ERROR_UNEXPECTED); |
|
6821 return nullptr; |
|
6822 } |
|
6823 |
|
6824 nsIDocument* doc = aElement->OwnerDoc(); |
|
6825 if (doc != this) { |
|
6826 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
|
6827 return nullptr; |
|
6828 } |
|
6829 |
|
6830 if (!mHasWarnedAboutBoxObjects && !aElement->IsXUL()) { |
|
6831 mHasWarnedAboutBoxObjects = true; |
|
6832 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
6833 NS_LITERAL_CSTRING("BoxObjects"), this, |
|
6834 nsContentUtils::eDOM_PROPERTIES, |
|
6835 "UseOfGetBoxObjectForWarning"); |
|
6836 } |
|
6837 |
|
6838 if (!mBoxObjectTable) { |
|
6839 mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(12); |
|
6840 } else { |
|
6841 nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement); |
|
6842 if (boxObject) { |
|
6843 return boxObject.forget(); |
|
6844 } |
|
6845 } |
|
6846 |
|
6847 int32_t namespaceID; |
|
6848 nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID); |
|
6849 |
|
6850 nsAutoCString contractID("@mozilla.org/layout/xul-boxobject"); |
|
6851 if (namespaceID == kNameSpaceID_XUL) { |
|
6852 if (tag == nsGkAtoms::browser || |
|
6853 tag == nsGkAtoms::editor || |
|
6854 tag == nsGkAtoms::iframe) |
|
6855 contractID += "-container"; |
|
6856 else if (tag == nsGkAtoms::menu) |
|
6857 contractID += "-menu"; |
|
6858 else if (tag == nsGkAtoms::popup || |
|
6859 tag == nsGkAtoms::menupopup || |
|
6860 tag == nsGkAtoms::panel || |
|
6861 tag == nsGkAtoms::tooltip) |
|
6862 contractID += "-popup"; |
|
6863 else if (tag == nsGkAtoms::tree) |
|
6864 contractID += "-tree"; |
|
6865 else if (tag == nsGkAtoms::listbox) |
|
6866 contractID += "-listbox"; |
|
6867 else if (tag == nsGkAtoms::scrollbox) |
|
6868 contractID += "-scrollbox"; |
|
6869 } |
|
6870 contractID += ";1"; |
|
6871 |
|
6872 nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get())); |
|
6873 if (!boxObject) { |
|
6874 aRv.Throw(NS_ERROR_FAILURE); |
|
6875 return nullptr; |
|
6876 } |
|
6877 |
|
6878 boxObject->Init(aElement); |
|
6879 |
|
6880 if (mBoxObjectTable) { |
|
6881 mBoxObjectTable->Put(aElement, boxObject.get()); |
|
6882 } |
|
6883 |
|
6884 return boxObject.forget(); |
|
6885 } |
|
6886 |
|
6887 void |
|
6888 nsDocument::ClearBoxObjectFor(nsIContent* aContent) |
|
6889 { |
|
6890 if (mBoxObjectTable) { |
|
6891 nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent); |
|
6892 if (boxObject) { |
|
6893 boxObject->Clear(); |
|
6894 mBoxObjectTable->Remove(aContent); |
|
6895 } |
|
6896 } |
|
6897 } |
|
6898 |
|
6899 void |
|
6900 nsDocument::FlushSkinBindings() |
|
6901 { |
|
6902 BindingManager()->FlushSkinBindings(); |
|
6903 } |
|
6904 |
|
6905 nsresult |
|
6906 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader) |
|
6907 { |
|
6908 mInitializableFrameLoaders.RemoveElement(aLoader); |
|
6909 // Don't even try to initialize. |
|
6910 if (mInDestructor) { |
|
6911 NS_WARNING("Trying to initialize a frame loader while" |
|
6912 "document is being deleted"); |
|
6913 return NS_ERROR_FAILURE; |
|
6914 } |
|
6915 |
|
6916 mInitializableFrameLoaders.AppendElement(aLoader); |
|
6917 if (!mFrameLoaderRunner) { |
|
6918 mFrameLoaderRunner = |
|
6919 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); |
|
6920 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); |
|
6921 nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
|
6922 } |
|
6923 return NS_OK; |
|
6924 } |
|
6925 |
|
6926 nsresult |
|
6927 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader) |
|
6928 { |
|
6929 mInitializableFrameLoaders.RemoveElement(aLoader); |
|
6930 if (mInDestructor) { |
|
6931 return NS_ERROR_FAILURE; |
|
6932 } |
|
6933 |
|
6934 mFinalizableFrameLoaders.AppendElement(aLoader); |
|
6935 if (!mFrameLoaderRunner) { |
|
6936 mFrameLoaderRunner = |
|
6937 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); |
|
6938 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); |
|
6939 nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
|
6940 } |
|
6941 return NS_OK; |
|
6942 } |
|
6943 |
|
6944 void |
|
6945 nsDocument::MaybeInitializeFinalizeFrameLoaders() |
|
6946 { |
|
6947 if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) { |
|
6948 // This method will be recalled when mUpdateNestLevel drops to 0, |
|
6949 // or when !mDelayFrameLoaderInitialization. |
|
6950 mFrameLoaderRunner = nullptr; |
|
6951 return; |
|
6952 } |
|
6953 |
|
6954 // We're not in an update, but it is not safe to run scripts, so |
|
6955 // postpone frameloader initialization and finalization. |
|
6956 if (!nsContentUtils::IsSafeToRunScript()) { |
|
6957 if (!mInDestructor && !mFrameLoaderRunner && |
|
6958 (mInitializableFrameLoaders.Length() || |
|
6959 mFinalizableFrameLoaders.Length())) { |
|
6960 mFrameLoaderRunner = |
|
6961 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); |
|
6962 nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
|
6963 } |
|
6964 return; |
|
6965 } |
|
6966 mFrameLoaderRunner = nullptr; |
|
6967 |
|
6968 // Don't use a temporary array for mInitializableFrameLoaders, because |
|
6969 // loading a frame may cause some other frameloader to be removed from the |
|
6970 // array. But be careful to keep the loader alive when starting the load! |
|
6971 while (mInitializableFrameLoaders.Length()) { |
|
6972 nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0]; |
|
6973 mInitializableFrameLoaders.RemoveElementAt(0); |
|
6974 NS_ASSERTION(loader, "null frameloader in the array?"); |
|
6975 loader->ReallyStartLoading(); |
|
6976 } |
|
6977 |
|
6978 uint32_t length = mFinalizableFrameLoaders.Length(); |
|
6979 if (length > 0) { |
|
6980 nsTArray<nsRefPtr<nsFrameLoader> > loaders; |
|
6981 mFinalizableFrameLoaders.SwapElements(loaders); |
|
6982 for (uint32_t i = 0; i < length; ++i) { |
|
6983 loaders[i]->Finalize(); |
|
6984 } |
|
6985 } |
|
6986 } |
|
6987 |
|
6988 void |
|
6989 nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell) |
|
6990 { |
|
6991 uint32_t length = mInitializableFrameLoaders.Length(); |
|
6992 for (uint32_t i = 0; i < length; ++i) { |
|
6993 if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) { |
|
6994 mInitializableFrameLoaders.RemoveElementAt(i); |
|
6995 return; |
|
6996 } |
|
6997 } |
|
6998 } |
|
6999 |
|
7000 bool |
|
7001 nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) |
|
7002 { |
|
7003 if (aShell) { |
|
7004 uint32_t length = mFinalizableFrameLoaders.Length(); |
|
7005 for (uint32_t i = 0; i < length; ++i) { |
|
7006 if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) { |
|
7007 return true; |
|
7008 } |
|
7009 } |
|
7010 } |
|
7011 return false; |
|
7012 } |
|
7013 |
|
7014 nsIDocument* |
|
7015 nsDocument::RequestExternalResource(nsIURI* aURI, |
|
7016 nsINode* aRequestingNode, |
|
7017 ExternalResourceLoad** aPendingLoad) |
|
7018 { |
|
7019 NS_PRECONDITION(aURI, "Must have a URI"); |
|
7020 NS_PRECONDITION(aRequestingNode, "Must have a node"); |
|
7021 if (mDisplayDocument) { |
|
7022 return mDisplayDocument->RequestExternalResource(aURI, |
|
7023 aRequestingNode, |
|
7024 aPendingLoad); |
|
7025 } |
|
7026 |
|
7027 return mExternalResourceMap.RequestResource(aURI, aRequestingNode, |
|
7028 this, aPendingLoad); |
|
7029 } |
|
7030 |
|
7031 void |
|
7032 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData) |
|
7033 { |
|
7034 mExternalResourceMap.EnumerateResources(aCallback, aData); |
|
7035 } |
|
7036 |
|
7037 nsSMILAnimationController* |
|
7038 nsDocument::GetAnimationController() |
|
7039 { |
|
7040 // We create the animation controller lazily because most documents won't want |
|
7041 // one and only SVG documents and the like will call this |
|
7042 if (mAnimationController) |
|
7043 return mAnimationController; |
|
7044 // Refuse to create an Animation Controller for data documents. |
|
7045 if (mLoadedAsData || mLoadedAsInteractiveData) |
|
7046 return nullptr; |
|
7047 |
|
7048 mAnimationController = new nsSMILAnimationController(this); |
|
7049 |
|
7050 // If there's a presContext then check the animation mode and pause if |
|
7051 // necessary. |
|
7052 nsIPresShell *shell = GetShell(); |
|
7053 if (mAnimationController && shell) { |
|
7054 nsPresContext *context = shell->GetPresContext(); |
|
7055 if (context && |
|
7056 context->ImageAnimationMode() == imgIContainer::kDontAnimMode) { |
|
7057 mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF); |
|
7058 } |
|
7059 } |
|
7060 |
|
7061 // If we're hidden (or being hidden), notify the newly-created animation |
|
7062 // controller. (Skip this check for SVG-as-an-image documents, though, |
|
7063 // because they don't get OnPageShow / OnPageHide calls). |
|
7064 if (!mIsShowing && !mIsBeingUsedAsImage) { |
|
7065 mAnimationController->OnPageHide(); |
|
7066 } |
|
7067 |
|
7068 return mAnimationController; |
|
7069 } |
|
7070 |
|
7071 /** |
|
7072 * Retrieve the "direction" property of the document. |
|
7073 * |
|
7074 * @lina 01/09/2001 |
|
7075 */ |
|
7076 NS_IMETHODIMP |
|
7077 nsDocument::GetDir(nsAString& aDirection) |
|
7078 { |
|
7079 nsIDocument::GetDir(aDirection); |
|
7080 return NS_OK; |
|
7081 } |
|
7082 |
|
7083 void |
|
7084 nsIDocument::GetDir(nsAString& aDirection) const |
|
7085 { |
|
7086 aDirection.Truncate(); |
|
7087 Element* rootElement = GetHtmlElement(); |
|
7088 if (rootElement) { |
|
7089 static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection); |
|
7090 } |
|
7091 } |
|
7092 |
|
7093 /** |
|
7094 * Set the "direction" property of the document. |
|
7095 * |
|
7096 * @lina 01/09/2001 |
|
7097 */ |
|
7098 NS_IMETHODIMP |
|
7099 nsDocument::SetDir(const nsAString& aDirection) |
|
7100 { |
|
7101 nsIDocument::SetDir(aDirection); |
|
7102 return NS_OK; |
|
7103 } |
|
7104 |
|
7105 void |
|
7106 nsIDocument::SetDir(const nsAString& aDirection) |
|
7107 { |
|
7108 Element* rootElement = GetHtmlElement(); |
|
7109 if (rootElement) { |
|
7110 rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, |
|
7111 aDirection, true); |
|
7112 } |
|
7113 } |
|
7114 |
|
7115 NS_IMETHODIMP |
|
7116 nsDocument::GetInputEncoding(nsAString& aInputEncoding) |
|
7117 { |
|
7118 nsIDocument::GetInputEncoding(aInputEncoding); |
|
7119 return NS_OK; |
|
7120 } |
|
7121 |
|
7122 void |
|
7123 nsIDocument::GetInputEncoding(nsAString& aInputEncoding) |
|
7124 { |
|
7125 // Not const function, because WarnOnceAbout is not a const method |
|
7126 WarnOnceAbout(eInputEncoding); |
|
7127 if (mHaveInputEncoding) { |
|
7128 return GetCharacterSet(aInputEncoding); |
|
7129 } |
|
7130 |
|
7131 SetDOMStringToNull(aInputEncoding); |
|
7132 } |
|
7133 |
|
7134 NS_IMETHODIMP |
|
7135 nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument) |
|
7136 { |
|
7137 *aSyntheticDocument = mIsSyntheticDocument; |
|
7138 return NS_OK; |
|
7139 } |
|
7140 |
|
7141 NS_IMETHODIMP |
|
7142 nsDocument::GetDocumentURI(nsAString& aDocumentURI) |
|
7143 { |
|
7144 nsString temp; |
|
7145 nsIDocument::GetDocumentURI(temp); |
|
7146 aDocumentURI = temp; |
|
7147 return NS_OK; |
|
7148 } |
|
7149 |
|
7150 void |
|
7151 nsIDocument::GetDocumentURI(nsString& aDocumentURI) const |
|
7152 { |
|
7153 if (mDocumentURI) { |
|
7154 nsAutoCString uri; |
|
7155 mDocumentURI->GetSpec(uri); |
|
7156 CopyUTF8toUTF16(uri, aDocumentURI); |
|
7157 } else { |
|
7158 aDocumentURI.Truncate(); |
|
7159 } |
|
7160 } |
|
7161 |
|
7162 // Alias of above |
|
7163 NS_IMETHODIMP |
|
7164 nsDocument::GetURL(nsAString& aURL) |
|
7165 { |
|
7166 return GetDocumentURI(aURL); |
|
7167 } |
|
7168 |
|
7169 void |
|
7170 nsIDocument::GetURL(nsString& aURL) const |
|
7171 { |
|
7172 return GetDocumentURI(aURL); |
|
7173 } |
|
7174 |
|
7175 void |
|
7176 nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI) const |
|
7177 { |
|
7178 if (!mChromeXHRDocURI || !nsContentUtils::IsCallerChrome()) { |
|
7179 return GetDocumentURI(aDocumentURI); |
|
7180 } |
|
7181 |
|
7182 nsAutoCString uri; |
|
7183 mChromeXHRDocURI->GetSpec(uri); |
|
7184 CopyUTF8toUTF16(uri, aDocumentURI); |
|
7185 } |
|
7186 |
|
7187 nsIURI* |
|
7188 nsIDocument::GetDocumentURIObject() const |
|
7189 { |
|
7190 if (!mChromeXHRDocURI) { |
|
7191 return GetDocumentURI(); |
|
7192 } |
|
7193 |
|
7194 return mChromeXHRDocURI; |
|
7195 } |
|
7196 |
|
7197 |
|
7198 // readonly attribute DOMString compatMode; |
|
7199 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are |
|
7200 // in almost standards or full standards mode. See bug 105640. This was |
|
7201 // implemented to match MSIE's compatMode property. |
|
7202 NS_IMETHODIMP |
|
7203 nsDocument::GetCompatMode(nsAString& aCompatMode) |
|
7204 { |
|
7205 nsString temp; |
|
7206 nsIDocument::GetCompatMode(temp); |
|
7207 aCompatMode = temp; |
|
7208 return NS_OK; |
|
7209 } |
|
7210 |
|
7211 void |
|
7212 nsIDocument::GetCompatMode(nsString& aCompatMode) const |
|
7213 { |
|
7214 NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks || |
|
7215 mCompatMode == eCompatibility_AlmostStandards || |
|
7216 mCompatMode == eCompatibility_FullStandards, |
|
7217 "mCompatMode is neither quirks nor strict for this document"); |
|
7218 |
|
7219 if (mCompatMode == eCompatibility_NavQuirks) { |
|
7220 aCompatMode.AssignLiteral("BackCompat"); |
|
7221 } else { |
|
7222 aCompatMode.AssignLiteral("CSS1Compat"); |
|
7223 } |
|
7224 } |
|
7225 |
|
7226 static void BlastSubtreeToPieces(nsINode *aNode); |
|
7227 |
|
7228 PLDHashOperator |
|
7229 BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg) |
|
7230 { |
|
7231 nsCOMPtr<nsIAttribute> *attr = |
|
7232 static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg); |
|
7233 |
|
7234 *attr = aData; |
|
7235 |
|
7236 NS_ASSERTION(attr->get(), |
|
7237 "non-nsIAttribute somehow made it into the hashmap?!"); |
|
7238 |
|
7239 return PL_DHASH_STOP; |
|
7240 } |
|
7241 |
|
7242 static void |
|
7243 BlastSubtreeToPieces(nsINode *aNode) |
|
7244 { |
|
7245 if (aNode->IsElement()) { |
|
7246 Element *element = aNode->AsElement(); |
|
7247 const nsDOMAttributeMap *map = element->GetAttributeMap(); |
|
7248 if (map) { |
|
7249 nsCOMPtr<nsIAttribute> attr; |
|
7250 while (map->Enumerate(BlastFunc, &attr) > 0) { |
|
7251 BlastSubtreeToPieces(attr); |
|
7252 |
|
7253 #ifdef DEBUG |
|
7254 nsresult rv = |
|
7255 #endif |
|
7256 element->UnsetAttr(attr->NodeInfo()->NamespaceID(), |
|
7257 attr->NodeInfo()->NameAtom(), |
|
7258 false); |
|
7259 |
|
7260 // XXX Should we abort here? |
|
7261 NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!"); |
|
7262 } |
|
7263 } |
|
7264 } |
|
7265 |
|
7266 uint32_t count = aNode->GetChildCount(); |
|
7267 for (uint32_t i = 0; i < count; ++i) { |
|
7268 BlastSubtreeToPieces(aNode->GetFirstChild()); |
|
7269 aNode->RemoveChildAt(0, false); |
|
7270 } |
|
7271 } |
|
7272 |
|
7273 |
|
7274 class nsUserDataCaller : public nsRunnable |
|
7275 { |
|
7276 public: |
|
7277 nsUserDataCaller(nsCOMArray<nsINode>& aNodesWithProperties, |
|
7278 nsIDocument* aOwnerDoc) |
|
7279 : mNodesWithProperties(aNodesWithProperties), |
|
7280 mOwnerDoc(aOwnerDoc) |
|
7281 { |
|
7282 } |
|
7283 |
|
7284 NS_IMETHOD Run() |
|
7285 { |
|
7286 nsNodeUtils::CallUserDataHandlers(mNodesWithProperties, mOwnerDoc, |
|
7287 nsIDOMUserDataHandler::NODE_ADOPTED, |
|
7288 false); |
|
7289 return NS_OK; |
|
7290 } |
|
7291 |
|
7292 private: |
|
7293 nsCOMArray<nsINode> mNodesWithProperties; |
|
7294 nsCOMPtr<nsIDocument> mOwnerDoc; |
|
7295 }; |
|
7296 |
|
7297 NS_IMETHODIMP |
|
7298 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult) |
|
7299 { |
|
7300 *aResult = nullptr; |
|
7301 |
|
7302 nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode); |
|
7303 NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED); |
|
7304 |
|
7305 ErrorResult rv; |
|
7306 nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv); |
|
7307 if (rv.Failed()) { |
|
7308 return rv.ErrorCode(); |
|
7309 } |
|
7310 |
|
7311 NS_ADDREF(*aResult = result->AsDOMNode()); |
|
7312 return NS_OK; |
|
7313 } |
|
7314 |
|
7315 nsINode* |
|
7316 nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) |
|
7317 { |
|
7318 nsINode* adoptedNode = &aAdoptedNode; |
|
7319 |
|
7320 // Scope firing mutation events so that we don't carry any state that |
|
7321 // might be stale |
|
7322 { |
|
7323 nsINode* parent = adoptedNode->GetParentNode(); |
|
7324 if (parent) { |
|
7325 nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent, |
|
7326 adoptedNode->OwnerDoc()); |
|
7327 } |
|
7328 } |
|
7329 |
|
7330 nsAutoScriptBlocker scriptBlocker; |
|
7331 |
|
7332 switch (adoptedNode->NodeType()) { |
|
7333 case nsIDOMNode::ATTRIBUTE_NODE: |
|
7334 { |
|
7335 // Remove from ownerElement. |
|
7336 nsRefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode); |
|
7337 |
|
7338 nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv); |
|
7339 if (rv.Failed()) { |
|
7340 return nullptr; |
|
7341 } |
|
7342 |
|
7343 if (ownerElement) { |
|
7344 nsRefPtr<Attr> newAttr = |
|
7345 ownerElement->RemoveAttributeNode(*adoptedAttr, rv); |
|
7346 if (rv.Failed()) { |
|
7347 return nullptr; |
|
7348 } |
|
7349 |
|
7350 newAttr.swap(adoptedAttr); |
|
7351 } |
|
7352 |
|
7353 break; |
|
7354 } |
|
7355 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: |
|
7356 case nsIDOMNode::ELEMENT_NODE: |
|
7357 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: |
|
7358 case nsIDOMNode::TEXT_NODE: |
|
7359 case nsIDOMNode::CDATA_SECTION_NODE: |
|
7360 case nsIDOMNode::COMMENT_NODE: |
|
7361 case nsIDOMNode::DOCUMENT_TYPE_NODE: |
|
7362 { |
|
7363 // Don't allow adopting a node's anonymous subtree out from under it. |
|
7364 if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) { |
|
7365 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
7366 return nullptr; |
|
7367 } |
|
7368 |
|
7369 // We don't want to adopt an element into its own contentDocument or into |
|
7370 // a descendant contentDocument, so we check if the frameElement of this |
|
7371 // document or any of its parents is the adopted node or one of its |
|
7372 // descendants. |
|
7373 nsIDocument *doc = this; |
|
7374 do { |
|
7375 nsPIDOMWindow *win = doc->GetWindow(); |
|
7376 if (win) { |
|
7377 nsCOMPtr<nsINode> node = |
|
7378 do_QueryInterface(win->GetFrameElementInternal()); |
|
7379 if (node && |
|
7380 nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) { |
|
7381 rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
|
7382 return nullptr; |
|
7383 } |
|
7384 } |
|
7385 } while ((doc = doc->GetParentDocument())); |
|
7386 |
|
7387 // Remove from parent. |
|
7388 nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode(); |
|
7389 if (parent) { |
|
7390 int32_t idx = parent->IndexOf(adoptedNode); |
|
7391 MOZ_ASSERT(idx >= 0); |
|
7392 parent->RemoveChildAt(idx, true); |
|
7393 } else { |
|
7394 MOZ_ASSERT(!adoptedNode->IsInDoc()); |
|
7395 |
|
7396 // If we're adopting a node that's not in a document, it might still |
|
7397 // have a binding applied. Remove the binding from the element now |
|
7398 // that it's getting adopted into a new document. |
|
7399 // TODO Fully tear down the binding. |
|
7400 adoptedNode->AsContent()->SetXBLBinding(nullptr); |
|
7401 } |
|
7402 |
|
7403 break; |
|
7404 } |
|
7405 case nsIDOMNode::DOCUMENT_NODE: |
|
7406 { |
|
7407 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
7408 return nullptr; |
|
7409 } |
|
7410 default: |
|
7411 { |
|
7412 NS_WARNING("Don't know how to adopt this nodetype for adoptNode."); |
|
7413 |
|
7414 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
7415 return nullptr; |
|
7416 } |
|
7417 } |
|
7418 |
|
7419 nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc(); |
|
7420 bool sameDocument = oldDocument == this; |
|
7421 |
|
7422 AutoJSContext cx; |
|
7423 JS::Rooted<JSObject*> newScope(cx, nullptr); |
|
7424 if (!sameDocument) { |
|
7425 newScope = GetWrapper(); |
|
7426 if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) { |
|
7427 // Make sure cx is in a semi-sane compartment before we call WrapNative. |
|
7428 // It's kind of irrelevant, given that we're passing aAllowWrapping = |
|
7429 // false, and documents should always insist on being wrapped in an |
|
7430 // canonical scope. But we try to pass something sane anyway. |
|
7431 JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject()); |
|
7432 JS::Rooted<JS::Value> v(cx); |
|
7433 rv = nsContentUtils::WrapNative(cx, this, this, &v, |
|
7434 /* aAllowWrapping = */ false); |
|
7435 if (rv.Failed()) |
|
7436 return nullptr; |
|
7437 newScope = &v.toObject(); |
|
7438 } |
|
7439 } |
|
7440 |
|
7441 nsCOMArray<nsINode> nodesWithProperties; |
|
7442 rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager, |
|
7443 newScope, nodesWithProperties); |
|
7444 if (rv.Failed()) { |
|
7445 // Disconnect all nodes from their parents, since some have the old document |
|
7446 // as their ownerDocument and some have this as their ownerDocument. |
|
7447 BlastSubtreeToPieces(adoptedNode); |
|
7448 |
|
7449 if (!sameDocument && oldDocument) { |
|
7450 uint32_t count = nodesWithProperties.Count(); |
|
7451 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) { |
|
7452 for (uint32_t i = 0; i < count; ++i) { |
|
7453 // Remove all properties. |
|
7454 oldDocument->PropertyTable(j)-> |
|
7455 DeleteAllPropertiesFor(nodesWithProperties[i]); |
|
7456 } |
|
7457 } |
|
7458 } |
|
7459 |
|
7460 return nullptr; |
|
7461 } |
|
7462 |
|
7463 uint32_t count = nodesWithProperties.Count(); |
|
7464 if (!sameDocument && oldDocument) { |
|
7465 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) { |
|
7466 nsPropertyTable *oldTable = oldDocument->PropertyTable(j); |
|
7467 nsPropertyTable *newTable = PropertyTable(j); |
|
7468 for (uint32_t i = 0; i < count; ++i) { |
|
7469 rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i], |
|
7470 newTable); |
|
7471 } |
|
7472 } |
|
7473 |
|
7474 if (rv.Failed()) { |
|
7475 // Disconnect all nodes from their parents. |
|
7476 BlastSubtreeToPieces(adoptedNode); |
|
7477 |
|
7478 return nullptr; |
|
7479 } |
|
7480 } |
|
7481 |
|
7482 if (nodesWithProperties.Count()) { |
|
7483 nsContentUtils::AddScriptRunner(new nsUserDataCaller(nodesWithProperties, |
|
7484 this)); |
|
7485 } |
|
7486 |
|
7487 NS_ASSERTION(adoptedNode->OwnerDoc() == this, |
|
7488 "Should still be in the document we just got adopted into"); |
|
7489 |
|
7490 return adoptedNode; |
|
7491 } |
|
7492 |
|
7493 nsViewportInfo |
|
7494 nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize) |
|
7495 { |
|
7496 // In cases where the width of the CSS viewport is less than or equal to the width |
|
7497 // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom |
|
7498 // behaviour. See bug 941995 for details. |
|
7499 |
|
7500 switch (mViewportType) { |
|
7501 case DisplayWidthHeight: |
|
7502 return nsViewportInfo(aDisplaySize); |
|
7503 case DisplayWidthHeightNoZoom: |
|
7504 return nsViewportInfo(aDisplaySize, /*allowZoom*/ false, /*allowDoubleTapZoom*/ false); |
|
7505 case Unknown: |
|
7506 { |
|
7507 nsAutoString viewport; |
|
7508 GetHeaderData(nsGkAtoms::viewport, viewport); |
|
7509 if (viewport.IsEmpty()) { |
|
7510 // If the docType specifies that we are on a site optimized for mobile, |
|
7511 // then we want to return specially crafted defaults for the viewport info. |
|
7512 nsCOMPtr<nsIDOMDocumentType> docType; |
|
7513 nsresult rv = GetDoctype(getter_AddRefs(docType)); |
|
7514 if (NS_SUCCEEDED(rv) && docType) { |
|
7515 nsAutoString docId; |
|
7516 rv = docType->GetPublicId(docId); |
|
7517 if (NS_SUCCEEDED(rv)) { |
|
7518 if ((docId.Find("WAP") != -1) || |
|
7519 (docId.Find("Mobile") != -1) || |
|
7520 (docId.Find("WML") != -1)) |
|
7521 { |
|
7522 // We're making an assumption that the docType can't change here |
|
7523 mViewportType = DisplayWidthHeight; |
|
7524 return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false); |
|
7525 } |
|
7526 } |
|
7527 } |
|
7528 |
|
7529 nsAutoString handheldFriendly; |
|
7530 GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly); |
|
7531 if (handheldFriendly.EqualsLiteral("true")) { |
|
7532 mViewportType = DisplayWidthHeight; |
|
7533 return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false); |
|
7534 } |
|
7535 |
|
7536 // Bug 940036. This is bad. When FirefoxOS was built, apps installed |
|
7537 // where not using the AsyncPanZoom code. As a result a lot of apps |
|
7538 // in the marketplace does not use it yet and instead are built to |
|
7539 // render correctly in FirefoxOS only. For a smooth transition the above |
|
7540 // code force installed apps to render as if they have a viewport with |
|
7541 // content="width=device-width, height=device-height, user-scalable=no". |
|
7542 // This could be safely remove once it is known that most apps in the |
|
7543 // marketplace use it and that users does not use an old version of the |
|
7544 // app that does not use it. |
|
7545 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
7546 if (docShell && docShell->GetIsApp()) { |
|
7547 nsString uri; |
|
7548 GetDocumentURI(uri); |
|
7549 if (!uri.EqualsLiteral("about:blank")) { |
|
7550 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
7551 NS_LITERAL_CSTRING("DOM"), this, |
|
7552 nsContentUtils::eDOM_PROPERTIES, |
|
7553 "ImplicitMetaViewportTagFallback"); |
|
7554 } |
|
7555 mViewportType = DisplayWidthHeightNoZoom; |
|
7556 return nsViewportInfo(aDisplaySize, /*allowZoom*/false, /*allowDoubleTapZoom*/false); |
|
7557 } |
|
7558 } |
|
7559 |
|
7560 nsAutoString minScaleStr; |
|
7561 GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr); |
|
7562 |
|
7563 nsresult errorCode; |
|
7564 mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode)); |
|
7565 |
|
7566 if (NS_FAILED(errorCode)) { |
|
7567 mScaleMinFloat = kViewportMinScale; |
|
7568 } |
|
7569 |
|
7570 mScaleMinFloat = mozilla::clamped( |
|
7571 mScaleMinFloat, kViewportMinScale, kViewportMaxScale); |
|
7572 |
|
7573 nsAutoString maxScaleStr; |
|
7574 GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr); |
|
7575 |
|
7576 // We define a special error code variable for the scale and max scale, |
|
7577 // because they are used later (see the width calculations). |
|
7578 nsresult scaleMaxErrorCode; |
|
7579 mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode)); |
|
7580 |
|
7581 if (NS_FAILED(scaleMaxErrorCode)) { |
|
7582 mScaleMaxFloat = kViewportMaxScale; |
|
7583 } |
|
7584 |
|
7585 mScaleMaxFloat = mozilla::clamped( |
|
7586 mScaleMaxFloat, kViewportMinScale, kViewportMaxScale); |
|
7587 |
|
7588 nsAutoString scaleStr; |
|
7589 GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr); |
|
7590 |
|
7591 nsresult scaleErrorCode; |
|
7592 mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode)); |
|
7593 |
|
7594 nsAutoString widthStr, heightStr; |
|
7595 |
|
7596 GetHeaderData(nsGkAtoms::viewport_height, heightStr); |
|
7597 GetHeaderData(nsGkAtoms::viewport_width, widthStr); |
|
7598 |
|
7599 mAutoSize = false; |
|
7600 |
|
7601 if (widthStr.EqualsLiteral("device-width")) { |
|
7602 mAutoSize = true; |
|
7603 } |
|
7604 |
|
7605 if (widthStr.IsEmpty() && |
|
7606 (heightStr.EqualsLiteral("device-height") || |
|
7607 (mScaleFloat.scale == 1.0))) |
|
7608 { |
|
7609 mAutoSize = true; |
|
7610 } |
|
7611 |
|
7612 nsresult widthErrorCode, heightErrorCode; |
|
7613 mViewportSize.width = widthStr.ToInteger(&widthErrorCode); |
|
7614 mViewportSize.height = heightStr.ToInteger(&heightErrorCode); |
|
7615 |
|
7616 // If width or height has not been set to a valid number by this point, |
|
7617 // fall back to a default value. |
|
7618 mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0); |
|
7619 mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0); |
|
7620 |
|
7621 mAllowZoom = true; |
|
7622 nsAutoString userScalable; |
|
7623 GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable); |
|
7624 |
|
7625 if ((userScalable.EqualsLiteral("0")) || |
|
7626 (userScalable.EqualsLiteral("no")) || |
|
7627 (userScalable.EqualsLiteral("false"))) { |
|
7628 mAllowZoom = false; |
|
7629 } |
|
7630 mAllowDoubleTapZoom = mAllowZoom; |
|
7631 |
|
7632 mScaleStrEmpty = scaleStr.IsEmpty(); |
|
7633 mWidthStrEmpty = widthStr.IsEmpty(); |
|
7634 mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode); |
|
7635 mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode); |
|
7636 |
|
7637 mViewportType = Specified; |
|
7638 } |
|
7639 case Specified: |
|
7640 default: |
|
7641 CSSIntSize size = mViewportSize; |
|
7642 |
|
7643 if (!mValidWidth) { |
|
7644 if (mValidHeight && !aDisplaySize.IsEmpty()) { |
|
7645 size.width = int32_t(size.height * aDisplaySize.width / aDisplaySize.height); |
|
7646 } else { |
|
7647 size.width = Preferences::GetInt("browser.viewport.desktopWidth", |
|
7648 kViewportDefaultScreenWidth); |
|
7649 } |
|
7650 } |
|
7651 |
|
7652 if (!mValidHeight) { |
|
7653 if (!aDisplaySize.IsEmpty()) { |
|
7654 size.height = int32_t(size.width * aDisplaySize.height / aDisplaySize.width); |
|
7655 } else { |
|
7656 size.height = size.width; |
|
7657 } |
|
7658 } |
|
7659 // Now convert the scale into device pixels per CSS pixel. |
|
7660 nsIWidget *widget = nsContentUtils::WidgetForDocument(this); |
|
7661 CSSToLayoutDeviceScale pixelRatio = widget ? widget->GetDefaultScale() |
|
7662 : CSSToLayoutDeviceScale(1.0f); |
|
7663 CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio; |
|
7664 CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio; |
|
7665 CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio; |
|
7666 |
|
7667 if (mAutoSize) { |
|
7668 // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size. |
|
7669 CSSToScreenScale defaultPixelScale = pixelRatio * LayoutDeviceToScreenScale(1.0f); |
|
7670 size = mozilla::gfx::RoundedToInt(ScreenSize(aDisplaySize) / defaultPixelScale); |
|
7671 } |
|
7672 |
|
7673 size.width = clamped(size.width, kViewportMinSize.width, kViewportMaxSize.width); |
|
7674 |
|
7675 // Also recalculate the default zoom, if it wasn't specified in the metadata, |
|
7676 // and the width is specified. |
|
7677 if (mScaleStrEmpty && !mWidthStrEmpty) { |
|
7678 CSSToScreenScale defaultScale(float(aDisplaySize.width) / float(size.width)); |
|
7679 scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale; |
|
7680 } |
|
7681 |
|
7682 size.height = clamped(size.height, kViewportMinSize.height, kViewportMaxSize.height); |
|
7683 |
|
7684 // We need to perform a conversion, but only if the initial or maximum |
|
7685 // scale were set explicitly by the user. |
|
7686 if (mValidScaleFloat) { |
|
7687 CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleFloat); |
|
7688 size.width = std::max(size.width, displaySize.width); |
|
7689 size.height = std::max(size.height, displaySize.height); |
|
7690 } else if (mValidMaxScale) { |
|
7691 CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleMaxFloat); |
|
7692 size.width = std::max(size.width, displaySize.width); |
|
7693 size.height = std::max(size.height, displaySize.height); |
|
7694 } |
|
7695 |
|
7696 return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size, |
|
7697 mAutoSize, mAllowZoom, mAllowDoubleTapZoom); |
|
7698 } |
|
7699 } |
|
7700 |
|
7701 EventListenerManager* |
|
7702 nsDocument::GetOrCreateListenerManager() |
|
7703 { |
|
7704 if (!mListenerManager) { |
|
7705 mListenerManager = |
|
7706 new EventListenerManager(static_cast<EventTarget*>(this)); |
|
7707 SetFlags(NODE_HAS_LISTENERMANAGER); |
|
7708 } |
|
7709 |
|
7710 return mListenerManager; |
|
7711 } |
|
7712 |
|
7713 EventListenerManager* |
|
7714 nsDocument::GetExistingListenerManager() const |
|
7715 { |
|
7716 return mListenerManager; |
|
7717 } |
|
7718 |
|
7719 nsresult |
|
7720 nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor) |
|
7721 { |
|
7722 aVisitor.mCanHandle = true; |
|
7723 // FIXME! This is a hack to make middle mouse paste working also in Editor. |
|
7724 // Bug 329119 |
|
7725 aVisitor.mForceContentDispatch = true; |
|
7726 |
|
7727 // Load events must not propagate to |window| object, see bug 335251. |
|
7728 if (aVisitor.mEvent->message != NS_LOAD) { |
|
7729 nsGlobalWindow* window = static_cast<nsGlobalWindow*>(GetWindow()); |
|
7730 aVisitor.mParentTarget = |
|
7731 window ? window->GetTargetForEventTargetChain() : nullptr; |
|
7732 } |
|
7733 return NS_OK; |
|
7734 } |
|
7735 |
|
7736 NS_IMETHODIMP |
|
7737 nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn) |
|
7738 { |
|
7739 NS_ENSURE_ARG_POINTER(aReturn); |
|
7740 ErrorResult rv; |
|
7741 *aReturn = nsIDocument::CreateEvent(aEventType, rv).take(); |
|
7742 return rv.ErrorCode(); |
|
7743 } |
|
7744 |
|
7745 already_AddRefed<Event> |
|
7746 nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const |
|
7747 { |
|
7748 nsIPresShell *shell = GetShell(); |
|
7749 |
|
7750 nsPresContext *presContext = nullptr; |
|
7751 |
|
7752 if (shell) { |
|
7753 // Retrieve the context |
|
7754 presContext = shell->GetPresContext(); |
|
7755 } |
|
7756 |
|
7757 // Create event even without presContext. |
|
7758 nsCOMPtr<nsIDOMEvent> ev; |
|
7759 rv = EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), |
|
7760 presContext, nullptr, aEventType, |
|
7761 getter_AddRefs(ev)); |
|
7762 if (!ev) { |
|
7763 return nullptr; |
|
7764 } |
|
7765 WidgetEvent* e = ev->GetInternalNSEvent(); |
|
7766 e->mFlags.mBubbles = false; |
|
7767 e->mFlags.mCancelable = false; |
|
7768 return dont_AddRef(ev.forget().take()->InternalDOMEvent()); |
|
7769 } |
|
7770 |
|
7771 void |
|
7772 nsDocument::FlushPendingNotifications(mozFlushType aType) |
|
7773 { |
|
7774 nsDocumentOnStack dos(this); |
|
7775 |
|
7776 // We need to flush the sink for non-HTML documents (because the XML |
|
7777 // parser still does insertion with deferred notifications). We |
|
7778 // also need to flush the sink if this is a layout-related flush, to |
|
7779 // make sure that layout is started as needed. But we can skip that |
|
7780 // part if we have no presshell or if it's already done an initial |
|
7781 // reflow. |
|
7782 if ((!IsHTML() || |
|
7783 (aType > Flush_ContentAndNotify && mPresShell && |
|
7784 !mPresShell->DidInitialize())) && |
|
7785 (mParser || mWeakSink)) { |
|
7786 nsCOMPtr<nsIContentSink> sink; |
|
7787 if (mParser) { |
|
7788 sink = mParser->GetContentSink(); |
|
7789 } else { |
|
7790 sink = do_QueryReferent(mWeakSink); |
|
7791 if (!sink) { |
|
7792 mWeakSink = nullptr; |
|
7793 } |
|
7794 } |
|
7795 // Determine if it is safe to flush the sink notifications |
|
7796 // by determining if it safe to flush all the presshells. |
|
7797 if (sink && (aType == Flush_Content || IsSafeToFlush())) { |
|
7798 sink->FlushPendingNotifications(aType); |
|
7799 } |
|
7800 } |
|
7801 |
|
7802 // Should we be flushing pending binding constructors in here? |
|
7803 |
|
7804 if (aType <= Flush_ContentAndNotify) { |
|
7805 // Nothing to do here |
|
7806 return; |
|
7807 } |
|
7808 |
|
7809 // If we have a parent we must flush the parent too to ensure that our |
|
7810 // container is reflowed if its size was changed. But if it's not safe to |
|
7811 // flush ourselves, then don't flush the parent, since that can cause things |
|
7812 // like resizes of our frame's widget, which we can't handle while flushing |
|
7813 // is unsafe. |
|
7814 // Since media queries mean that a size change of our container can |
|
7815 // affect style, we need to promote a style flush on ourself to a |
|
7816 // layout flush on our parent, since we need our container to be the |
|
7817 // correct size to determine the correct style. |
|
7818 if (mParentDocument && IsSafeToFlush()) { |
|
7819 mozFlushType parentType = aType; |
|
7820 if (aType >= Flush_Style) |
|
7821 parentType = std::max(Flush_Layout, aType); |
|
7822 mParentDocument->FlushPendingNotifications(parentType); |
|
7823 } |
|
7824 |
|
7825 // We can optimize away getting our presshell and calling |
|
7826 // FlushPendingNotifications on it if we don't need a flush of the sort we're |
|
7827 // looking at. The one exception is if mInFlush is true, because in that |
|
7828 // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false |
|
7829 // already but the presshell hasn't actually done the corresponding work yet. |
|
7830 // So if mInFlush and reentering this code, we need to flush the presshell. |
|
7831 if (mNeedStyleFlush || |
|
7832 (mNeedLayoutFlush && aType >= Flush_InterruptibleLayout) || |
|
7833 aType >= Flush_Display || |
|
7834 mInFlush) { |
|
7835 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
7836 if (shell) { |
|
7837 mNeedStyleFlush = false; |
|
7838 mNeedLayoutFlush = mNeedLayoutFlush && (aType < Flush_InterruptibleLayout); |
|
7839 // mInFlush is a bitfield, so can't us AutoRestore here. But we |
|
7840 // need to keep track of multi-level reentry correctly, so need |
|
7841 // to restore the old mInFlush value. |
|
7842 bool oldInFlush = mInFlush; |
|
7843 mInFlush = true; |
|
7844 shell->FlushPendingNotifications(aType); |
|
7845 mInFlush = oldInFlush; |
|
7846 } |
|
7847 } |
|
7848 } |
|
7849 |
|
7850 static bool |
|
7851 Copy(nsIDocument* aDocument, void* aData) |
|
7852 { |
|
7853 nsTArray<nsCOMPtr<nsIDocument> >* resources = |
|
7854 static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData); |
|
7855 resources->AppendElement(aDocument); |
|
7856 return true; |
|
7857 } |
|
7858 |
|
7859 void |
|
7860 nsDocument::FlushExternalResources(mozFlushType aType) |
|
7861 { |
|
7862 NS_ASSERTION(aType >= Flush_Style, |
|
7863 "should only need to flush for style or higher in external resources"); |
|
7864 if (GetDisplayDocument()) { |
|
7865 return; |
|
7866 } |
|
7867 nsTArray<nsCOMPtr<nsIDocument> > resources; |
|
7868 EnumerateExternalResources(Copy, &resources); |
|
7869 |
|
7870 for (uint32_t i = 0; i < resources.Length(); i++) { |
|
7871 resources[i]->FlushPendingNotifications(aType); |
|
7872 } |
|
7873 } |
|
7874 |
|
7875 void |
|
7876 nsDocument::SetXMLDeclaration(const char16_t *aVersion, |
|
7877 const char16_t *aEncoding, |
|
7878 const int32_t aStandalone) |
|
7879 { |
|
7880 if (!aVersion || *aVersion == '\0') { |
|
7881 mXMLDeclarationBits = 0; |
|
7882 return; |
|
7883 } |
|
7884 |
|
7885 mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS; |
|
7886 |
|
7887 if (aEncoding && *aEncoding != '\0') { |
|
7888 mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS; |
|
7889 } |
|
7890 |
|
7891 if (aStandalone == 1) { |
|
7892 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS | |
|
7893 XML_DECLARATION_BITS_STANDALONE_YES; |
|
7894 } |
|
7895 else if (aStandalone == 0) { |
|
7896 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS; |
|
7897 } |
|
7898 } |
|
7899 |
|
7900 void |
|
7901 nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding, |
|
7902 nsAString& aStandalone) |
|
7903 { |
|
7904 aVersion.Truncate(); |
|
7905 aEncoding.Truncate(); |
|
7906 aStandalone.Truncate(); |
|
7907 |
|
7908 if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) { |
|
7909 return; |
|
7910 } |
|
7911 |
|
7912 // always until we start supporting 1.1 etc. |
|
7913 aVersion.AssignLiteral("1.0"); |
|
7914 |
|
7915 if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) { |
|
7916 // This is what we have stored, not necessarily what was written |
|
7917 // in the original |
|
7918 GetCharacterSet(aEncoding); |
|
7919 } |
|
7920 |
|
7921 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) { |
|
7922 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) { |
|
7923 aStandalone.AssignLiteral("yes"); |
|
7924 } else { |
|
7925 aStandalone.AssignLiteral("no"); |
|
7926 } |
|
7927 } |
|
7928 } |
|
7929 |
|
7930 bool |
|
7931 nsDocument::IsScriptEnabled() |
|
7932 { |
|
7933 // If this document is sandboxed without 'allow-scripts' |
|
7934 // script is not enabled |
|
7935 if (mSandboxFlags & SANDBOXED_SCRIPTS) { |
|
7936 return false; |
|
7937 } |
|
7938 |
|
7939 nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); |
|
7940 NS_ENSURE_TRUE(sm, false); |
|
7941 |
|
7942 nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow()); |
|
7943 NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false); |
|
7944 |
|
7945 return sm->ScriptAllowed(globalObject->GetGlobalJSObject()); |
|
7946 } |
|
7947 |
|
7948 nsRadioGroupStruct* |
|
7949 nsDocument::GetRadioGroupInternal(const nsAString& aName) const |
|
7950 { |
|
7951 #ifdef DEBUG |
|
7952 if (IsHTML()) { |
|
7953 nsAutoString lcName; |
|
7954 ToLowerCase(aName, lcName); |
|
7955 MOZ_ASSERT(aName == lcName); |
|
7956 } |
|
7957 #endif |
|
7958 |
|
7959 nsRadioGroupStruct* radioGroup; |
|
7960 if (!mRadioGroups.Get(aName, &radioGroup)) { |
|
7961 return nullptr; |
|
7962 } |
|
7963 |
|
7964 return radioGroup; |
|
7965 } |
|
7966 |
|
7967 nsRadioGroupStruct* |
|
7968 nsDocument::GetRadioGroup(const nsAString& aName) const |
|
7969 { |
|
7970 nsAutoString tmKey(aName); |
|
7971 if (IsHTML()) { |
|
7972 ToLowerCase(tmKey); //should case-insensitive. |
|
7973 } |
|
7974 |
|
7975 return GetRadioGroupInternal(tmKey); |
|
7976 } |
|
7977 |
|
7978 nsRadioGroupStruct* |
|
7979 nsDocument::GetOrCreateRadioGroup(const nsAString& aName) |
|
7980 { |
|
7981 nsAutoString tmKey(aName); |
|
7982 if (IsHTML()) { |
|
7983 ToLowerCase(tmKey); //should case-insensitive. |
|
7984 } |
|
7985 |
|
7986 if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) { |
|
7987 return radioGroup; |
|
7988 } |
|
7989 |
|
7990 nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct()); |
|
7991 mRadioGroups.Put(tmKey, newRadioGroup); |
|
7992 |
|
7993 return newRadioGroup.forget(); |
|
7994 } |
|
7995 |
|
7996 void |
|
7997 nsDocument::SetCurrentRadioButton(const nsAString& aName, |
|
7998 HTMLInputElement* aRadio) |
|
7999 { |
|
8000 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8001 radioGroup->mSelectedRadioButton = aRadio; |
|
8002 } |
|
8003 |
|
8004 HTMLInputElement* |
|
8005 nsDocument::GetCurrentRadioButton(const nsAString& aName) |
|
8006 { |
|
8007 return GetOrCreateRadioGroup(aName)->mSelectedRadioButton; |
|
8008 } |
|
8009 |
|
8010 NS_IMETHODIMP |
|
8011 nsDocument::GetNextRadioButton(const nsAString& aName, |
|
8012 const bool aPrevious, |
|
8013 HTMLInputElement* aFocusedRadio, |
|
8014 HTMLInputElement** aRadioOut) |
|
8015 { |
|
8016 // XXX Can we combine the HTML radio button method impls of |
|
8017 // nsDocument and nsHTMLFormControl? |
|
8018 // XXX Why is HTML radio button stuff in nsDocument, as |
|
8019 // opposed to nsHTMLDocument? |
|
8020 *aRadioOut = nullptr; |
|
8021 |
|
8022 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8023 |
|
8024 // Return the radio button relative to the focused radio button. |
|
8025 // If no radio is focused, get the radio relative to the selected one. |
|
8026 nsRefPtr<HTMLInputElement> currentRadio; |
|
8027 if (aFocusedRadio) { |
|
8028 currentRadio = aFocusedRadio; |
|
8029 } |
|
8030 else { |
|
8031 currentRadio = radioGroup->mSelectedRadioButton; |
|
8032 if (!currentRadio) { |
|
8033 return NS_ERROR_FAILURE; |
|
8034 } |
|
8035 } |
|
8036 int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio); |
|
8037 if (index < 0) { |
|
8038 return NS_ERROR_FAILURE; |
|
8039 } |
|
8040 |
|
8041 int32_t numRadios = radioGroup->mRadioButtons.Count(); |
|
8042 nsRefPtr<HTMLInputElement> radio; |
|
8043 do { |
|
8044 if (aPrevious) { |
|
8045 if (--index < 0) { |
|
8046 index = numRadios -1; |
|
8047 } |
|
8048 } |
|
8049 else if (++index >= numRadios) { |
|
8050 index = 0; |
|
8051 } |
|
8052 NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTML(nsGkAtoms::input), |
|
8053 "mRadioButtons holding a non-radio button"); |
|
8054 radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]); |
|
8055 } while (radio->Disabled() && radio != currentRadio); |
|
8056 |
|
8057 radio.forget(aRadioOut); |
|
8058 return NS_OK; |
|
8059 } |
|
8060 |
|
8061 void |
|
8062 nsDocument::AddToRadioGroup(const nsAString& aName, |
|
8063 nsIFormControl* aRadio) |
|
8064 { |
|
8065 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8066 radioGroup->mRadioButtons.AppendObject(aRadio); |
|
8067 |
|
8068 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio); |
|
8069 NS_ASSERTION(element, "radio controls have to be content elements"); |
|
8070 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { |
|
8071 radioGroup->mRequiredRadioCount++; |
|
8072 } |
|
8073 } |
|
8074 |
|
8075 void |
|
8076 nsDocument::RemoveFromRadioGroup(const nsAString& aName, |
|
8077 nsIFormControl* aRadio) |
|
8078 { |
|
8079 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8080 radioGroup->mRadioButtons.RemoveObject(aRadio); |
|
8081 |
|
8082 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio); |
|
8083 NS_ASSERTION(element, "radio controls have to be content elements"); |
|
8084 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { |
|
8085 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0, |
|
8086 "mRequiredRadioCount about to wrap below 0!"); |
|
8087 radioGroup->mRequiredRadioCount--; |
|
8088 } |
|
8089 } |
|
8090 |
|
8091 NS_IMETHODIMP |
|
8092 nsDocument::WalkRadioGroup(const nsAString& aName, |
|
8093 nsIRadioVisitor* aVisitor, |
|
8094 bool aFlushContent) |
|
8095 { |
|
8096 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8097 |
|
8098 for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) { |
|
8099 if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) { |
|
8100 return NS_OK; |
|
8101 } |
|
8102 } |
|
8103 |
|
8104 return NS_OK; |
|
8105 } |
|
8106 |
|
8107 uint32_t |
|
8108 nsDocument::GetRequiredRadioCount(const nsAString& aName) const |
|
8109 { |
|
8110 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName); |
|
8111 return radioGroup ? radioGroup->mRequiredRadioCount : 0; |
|
8112 } |
|
8113 |
|
8114 void |
|
8115 nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio) |
|
8116 { |
|
8117 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8118 |
|
8119 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio); |
|
8120 NS_ASSERTION(element, "radio controls have to be content elements"); |
|
8121 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { |
|
8122 radioGroup->mRequiredRadioCount++; |
|
8123 } else { |
|
8124 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0, |
|
8125 "mRequiredRadioCount about to wrap below 0!"); |
|
8126 radioGroup->mRequiredRadioCount--; |
|
8127 } |
|
8128 } |
|
8129 |
|
8130 bool |
|
8131 nsDocument::GetValueMissingState(const nsAString& aName) const |
|
8132 { |
|
8133 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName); |
|
8134 return radioGroup && radioGroup->mGroupSuffersFromValueMissing; |
|
8135 } |
|
8136 |
|
8137 void |
|
8138 nsDocument::SetValueMissingState(const nsAString& aName, bool aValue) |
|
8139 { |
|
8140 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
|
8141 radioGroup->mGroupSuffersFromValueMissing = aValue; |
|
8142 } |
|
8143 |
|
8144 void |
|
8145 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) |
|
8146 { |
|
8147 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); |
|
8148 PRTime modDate = 0; |
|
8149 nsresult rv; |
|
8150 |
|
8151 if (httpChannel) { |
|
8152 nsAutoCString tmp; |
|
8153 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), |
|
8154 tmp); |
|
8155 |
|
8156 if (NS_SUCCEEDED(rv)) { |
|
8157 PRTime time; |
|
8158 PRStatus st = PR_ParseTimeString(tmp.get(), true, &time); |
|
8159 if (st == PR_SUCCESS) { |
|
8160 modDate = time; |
|
8161 } |
|
8162 } |
|
8163 |
|
8164 // The misspelled key 'referer' is as per the HTTP spec |
|
8165 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"), |
|
8166 mReferrer); |
|
8167 if (NS_FAILED(rv)) { |
|
8168 mReferrer.Truncate(); |
|
8169 } |
|
8170 |
|
8171 static const char *const headers[] = { |
|
8172 "default-style", |
|
8173 "content-style-type", |
|
8174 "content-language", |
|
8175 "content-disposition", |
|
8176 "refresh", |
|
8177 "x-dns-prefetch-control", |
|
8178 "x-frame-options", |
|
8179 // add more http headers if you need |
|
8180 // XXXbz don't add content-location support without reading bug |
|
8181 // 238654 and its dependencies/dups first. |
|
8182 0 |
|
8183 }; |
|
8184 |
|
8185 nsAutoCString headerVal; |
|
8186 const char *const *name = headers; |
|
8187 while (*name) { |
|
8188 rv = |
|
8189 httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal); |
|
8190 if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) { |
|
8191 nsCOMPtr<nsIAtom> key = do_GetAtom(*name); |
|
8192 SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal)); |
|
8193 } |
|
8194 ++name; |
|
8195 } |
|
8196 } else { |
|
8197 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel); |
|
8198 if (fileChannel) { |
|
8199 nsCOMPtr<nsIFile> file; |
|
8200 fileChannel->GetFile(getter_AddRefs(file)); |
|
8201 if (file) { |
|
8202 PRTime msecs; |
|
8203 rv = file->GetLastModifiedTime(&msecs); |
|
8204 |
|
8205 if (NS_SUCCEEDED(rv)) { |
|
8206 modDate = msecs * int64_t(PR_USEC_PER_MSEC); |
|
8207 } |
|
8208 } |
|
8209 } else { |
|
8210 nsAutoCString contentDisp; |
|
8211 rv = aChannel->GetContentDispositionHeader(contentDisp); |
|
8212 if (NS_SUCCEEDED(rv)) { |
|
8213 SetHeaderData(nsGkAtoms::headerContentDisposition, |
|
8214 NS_ConvertASCIItoUTF16(contentDisp)); |
|
8215 } |
|
8216 } |
|
8217 } |
|
8218 |
|
8219 if (modDate == 0) { |
|
8220 // We got nothing from our attempt to ask nsIFileChannel and |
|
8221 // nsIHttpChannel for the last modified time. Return the current |
|
8222 // time. |
|
8223 modDate = PR_Now(); |
|
8224 } |
|
8225 |
|
8226 mLastModified.Truncate(); |
|
8227 if (modDate != 0) { |
|
8228 PRExplodedTime prtime; |
|
8229 PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime); |
|
8230 // "MM/DD/YYYY hh:mm:ss" |
|
8231 char formatedTime[24]; |
|
8232 if (PR_snprintf(formatedTime, sizeof(formatedTime), |
|
8233 "%02ld/%02ld/%04hd %02ld:%02ld:%02ld", |
|
8234 prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year, |
|
8235 prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) { |
|
8236 CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified); |
|
8237 } |
|
8238 } |
|
8239 } |
|
8240 |
|
8241 nsresult |
|
8242 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix, int32_t aNamespaceID, |
|
8243 nsIContent **aResult) |
|
8244 { |
|
8245 #ifdef DEBUG |
|
8246 nsAutoString qName; |
|
8247 if (aPrefix) { |
|
8248 aPrefix->ToString(qName); |
|
8249 qName.Append(':'); |
|
8250 } |
|
8251 qName.Append(aName); |
|
8252 |
|
8253 // Note: "a:b:c" is a valid name in non-namespaces XML, and |
|
8254 // nsDocument::CreateElement can call us with such a name and no prefix, |
|
8255 // which would cause an error if we just used true here. |
|
8256 bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID(); |
|
8257 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)), |
|
8258 "Don't pass invalid prefixes to nsDocument::CreateElem, " |
|
8259 "check caller."); |
|
8260 #endif |
|
8261 |
|
8262 *aResult = nullptr; |
|
8263 |
|
8264 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
8265 mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID, |
|
8266 nsIDOMNode::ELEMENT_NODE, |
|
8267 getter_AddRefs(nodeInfo)); |
|
8268 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); |
|
8269 |
|
8270 nsCOMPtr<Element> element; |
|
8271 nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), |
|
8272 NOT_FROM_PARSER); |
|
8273 element.forget(aResult); |
|
8274 return rv; |
|
8275 } |
|
8276 |
|
8277 bool |
|
8278 nsDocument::IsSafeToFlush() const |
|
8279 { |
|
8280 nsIPresShell* shell = GetShell(); |
|
8281 if (!shell) |
|
8282 return true; |
|
8283 |
|
8284 return shell->IsSafeToFlush(); |
|
8285 } |
|
8286 |
|
8287 void |
|
8288 nsDocument::Sanitize() |
|
8289 { |
|
8290 // Sanitize the document by resetting all password fields and any form |
|
8291 // fields with autocomplete=off to their default values. We do this now, |
|
8292 // instead of when the presentation is restored, to offer some protection |
|
8293 // in case there is ever an exploit that allows a cached document to be |
|
8294 // accessed from a different document. |
|
8295 |
|
8296 // First locate all input elements, regardless of whether they are |
|
8297 // in a form, and reset the password and autocomplete=off elements. |
|
8298 |
|
8299 nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input")); |
|
8300 |
|
8301 nsCOMPtr<nsIContent> item; |
|
8302 nsAutoString value; |
|
8303 |
|
8304 uint32_t length = nodes->Length(true); |
|
8305 for (uint32_t i = 0; i < length; ++i) { |
|
8306 NS_ASSERTION(nodes->Item(i), "null item in node list!"); |
|
8307 |
|
8308 nsRefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i)); |
|
8309 if (!input) |
|
8310 continue; |
|
8311 |
|
8312 bool resetValue = false; |
|
8313 |
|
8314 input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value); |
|
8315 if (value.LowerCaseEqualsLiteral("off")) { |
|
8316 resetValue = true; |
|
8317 } else { |
|
8318 input->GetType(value); |
|
8319 if (value.LowerCaseEqualsLiteral("password")) |
|
8320 resetValue = true; |
|
8321 } |
|
8322 |
|
8323 if (resetValue) { |
|
8324 input->Reset(); |
|
8325 } |
|
8326 } |
|
8327 |
|
8328 // Now locate all _form_ elements that have autocomplete=off and reset them |
|
8329 nodes = GetElementsByTagName(NS_LITERAL_STRING("form")); |
|
8330 |
|
8331 length = nodes->Length(true); |
|
8332 for (uint32_t i = 0; i < length; ++i) { |
|
8333 NS_ASSERTION(nodes->Item(i), "null item in nodelist"); |
|
8334 |
|
8335 nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i)); |
|
8336 if (!form) |
|
8337 continue; |
|
8338 |
|
8339 nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None, |
|
8340 nsGkAtoms::autocomplete, value); |
|
8341 if (value.LowerCaseEqualsLiteral("off")) |
|
8342 form->Reset(); |
|
8343 } |
|
8344 } |
|
8345 |
|
8346 struct SubDocEnumArgs |
|
8347 { |
|
8348 nsIDocument::nsSubDocEnumFunc callback; |
|
8349 void *data; |
|
8350 }; |
|
8351 |
|
8352 static PLDHashOperator |
|
8353 SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr, |
|
8354 uint32_t number, void *arg) |
|
8355 { |
|
8356 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
|
8357 SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg); |
|
8358 |
|
8359 nsIDocument *subdoc = entry->mSubDocument; |
|
8360 bool next = subdoc ? args->callback(subdoc, args->data) : true; |
|
8361 |
|
8362 return next ? PL_DHASH_NEXT : PL_DHASH_STOP; |
|
8363 } |
|
8364 |
|
8365 void |
|
8366 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData) |
|
8367 { |
|
8368 if (mSubDocuments) { |
|
8369 SubDocEnumArgs args = { aCallback, aData }; |
|
8370 PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args); |
|
8371 } |
|
8372 } |
|
8373 |
|
8374 static PLDHashOperator |
|
8375 CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr, |
|
8376 uint32_t number, void *arg) |
|
8377 { |
|
8378 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
|
8379 bool *canCacheArg = static_cast<bool*>(arg); |
|
8380 |
|
8381 nsIDocument *subdoc = entry->mSubDocument; |
|
8382 |
|
8383 // The aIgnoreRequest we were passed is only for us, so don't pass it on. |
|
8384 bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false; |
|
8385 if (!canCache) { |
|
8386 *canCacheArg = false; |
|
8387 return PL_DHASH_STOP; |
|
8388 } |
|
8389 |
|
8390 return PL_DHASH_NEXT; |
|
8391 } |
|
8392 |
|
8393 #ifdef DEBUG_bryner |
|
8394 #define DEBUG_PAGE_CACHE |
|
8395 #endif |
|
8396 |
|
8397 bool |
|
8398 nsDocument::CanSavePresentation(nsIRequest *aNewRequest) |
|
8399 { |
|
8400 if (EventHandlingSuppressed()) { |
|
8401 return false; |
|
8402 } |
|
8403 |
|
8404 nsPIDOMWindow* win = GetInnerWindow(); |
|
8405 if (win && win->TimeoutSuspendCount()) { |
|
8406 return false; |
|
8407 } |
|
8408 |
|
8409 // Check our event listener manager for unload/beforeunload listeners. |
|
8410 nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject); |
|
8411 if (piTarget) { |
|
8412 EventListenerManager* manager = piTarget->GetExistingListenerManager(); |
|
8413 if (manager && manager->HasUnloadListeners()) { |
|
8414 return false; |
|
8415 } |
|
8416 } |
|
8417 |
|
8418 // Check if we have pending network requests |
|
8419 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
|
8420 if (loadGroup) { |
|
8421 nsCOMPtr<nsISimpleEnumerator> requests; |
|
8422 loadGroup->GetRequests(getter_AddRefs(requests)); |
|
8423 |
|
8424 bool hasMore = false; |
|
8425 |
|
8426 // We want to bail out if we have any requests other than aNewRequest (or |
|
8427 // in the case when aNewRequest is a part of a multipart response the base |
|
8428 // channel the multipart response is coming in on). |
|
8429 nsCOMPtr<nsIChannel> baseChannel; |
|
8430 nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest)); |
|
8431 if (part) { |
|
8432 part->GetBaseChannel(getter_AddRefs(baseChannel)); |
|
8433 } |
|
8434 |
|
8435 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { |
|
8436 nsCOMPtr<nsISupports> elem; |
|
8437 requests->GetNext(getter_AddRefs(elem)); |
|
8438 |
|
8439 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem); |
|
8440 if (request && request != aNewRequest && request != baseChannel) { |
|
8441 #ifdef DEBUG_PAGE_CACHE |
|
8442 nsAutoCString requestName, docSpec; |
|
8443 request->GetName(requestName); |
|
8444 if (mDocumentURI) |
|
8445 mDocumentURI->GetSpec(docSpec); |
|
8446 |
|
8447 printf("document %s has request %s\n", |
|
8448 docSpec.get(), requestName.get()); |
|
8449 #endif |
|
8450 return false; |
|
8451 } |
|
8452 } |
|
8453 } |
|
8454 |
|
8455 // Check if we have running offline storage transactions |
|
8456 quota::QuotaManager* quotaManager = |
|
8457 win ? quota::QuotaManager::Get() : nullptr; |
|
8458 if (quotaManager && quotaManager->HasOpenTransactions(win)) { |
|
8459 return false; |
|
8460 } |
|
8461 |
|
8462 #ifdef MOZ_MEDIA_NAVIGATOR |
|
8463 // Check if we have active GetUserMedia use |
|
8464 if (MediaManager::Exists() && win && |
|
8465 MediaManager::Get()->IsWindowStillActive(win->WindowID())) { |
|
8466 return false; |
|
8467 } |
|
8468 #endif // MOZ_MEDIA_NAVIGATOR |
|
8469 |
|
8470 #ifdef MOZ_WEBRTC |
|
8471 // Check if we have active PeerConnections |
|
8472 nsCOMPtr<IPeerConnectionManager> pcManager = |
|
8473 do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID); |
|
8474 |
|
8475 if (pcManager && win) { |
|
8476 bool active; |
|
8477 pcManager->HasActivePeerConnection(win->WindowID(), &active); |
|
8478 if (active) { |
|
8479 return false; |
|
8480 } |
|
8481 } |
|
8482 #endif // MOZ_WEBRTC |
|
8483 |
|
8484 bool canCache = true; |
|
8485 if (mSubDocuments) |
|
8486 PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache); |
|
8487 |
|
8488 return canCache; |
|
8489 } |
|
8490 |
|
8491 void |
|
8492 nsDocument::Destroy() |
|
8493 { |
|
8494 // The ContentViewer wants to release the document now. So, tell our content |
|
8495 // to drop any references to the document so that it can be destroyed. |
|
8496 if (mIsGoingAway) |
|
8497 return; |
|
8498 |
|
8499 mIsGoingAway = true; |
|
8500 |
|
8501 RemovedFromDocShell(); |
|
8502 |
|
8503 bool oldVal = mInUnlinkOrDeletion; |
|
8504 mInUnlinkOrDeletion = true; |
|
8505 uint32_t i, count = mChildren.ChildCount(); |
|
8506 for (i = 0; i < count; ++i) { |
|
8507 mChildren.ChildAt(i)->DestroyContent(); |
|
8508 } |
|
8509 mInUnlinkOrDeletion = oldVal; |
|
8510 |
|
8511 mLayoutHistoryState = nullptr; |
|
8512 |
|
8513 // Shut down our external resource map. We might not need this for |
|
8514 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but |
|
8515 // tearing down all those frame trees right now is the right thing to do. |
|
8516 mExternalResourceMap.Shutdown(); |
|
8517 |
|
8518 mRegistry = nullptr; |
|
8519 |
|
8520 // XXX We really should let cycle collection do this, but that currently still |
|
8521 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). |
|
8522 ReleaseWrapper(static_cast<nsINode*>(this)); |
|
8523 } |
|
8524 |
|
8525 void |
|
8526 nsDocument::RemovedFromDocShell() |
|
8527 { |
|
8528 if (mRemovedFromDocShell) |
|
8529 return; |
|
8530 |
|
8531 mRemovedFromDocShell = true; |
|
8532 EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
|
8533 |
|
8534 uint32_t i, count = mChildren.ChildCount(); |
|
8535 for (i = 0; i < count; ++i) { |
|
8536 mChildren.ChildAt(i)->SaveSubtreeState(); |
|
8537 } |
|
8538 } |
|
8539 |
|
8540 already_AddRefed<nsILayoutHistoryState> |
|
8541 nsDocument::GetLayoutHistoryState() const |
|
8542 { |
|
8543 nsCOMPtr<nsILayoutHistoryState> state; |
|
8544 if (!mScriptGlobalObject) { |
|
8545 state = mLayoutHistoryState; |
|
8546 } else { |
|
8547 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
8548 if (docShell) { |
|
8549 docShell->GetLayoutHistoryState(getter_AddRefs(state)); |
|
8550 } |
|
8551 } |
|
8552 |
|
8553 return state.forget(); |
|
8554 } |
|
8555 |
|
8556 void |
|
8557 nsDocument::EnsureOnloadBlocker() |
|
8558 { |
|
8559 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
|
8560 // -- it's not ours. |
|
8561 if (mOnloadBlockCount != 0 && mScriptGlobalObject) { |
|
8562 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
|
8563 if (loadGroup) { |
|
8564 // Check first to see if mOnloadBlocker is in the loadgroup. |
|
8565 nsCOMPtr<nsISimpleEnumerator> requests; |
|
8566 loadGroup->GetRequests(getter_AddRefs(requests)); |
|
8567 |
|
8568 bool hasMore = false; |
|
8569 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { |
|
8570 nsCOMPtr<nsISupports> elem; |
|
8571 requests->GetNext(getter_AddRefs(elem)); |
|
8572 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem); |
|
8573 if (request && request == mOnloadBlocker) { |
|
8574 return; |
|
8575 } |
|
8576 } |
|
8577 |
|
8578 // Not in the loadgroup, so add it. |
|
8579 loadGroup->AddRequest(mOnloadBlocker, nullptr); |
|
8580 } |
|
8581 } |
|
8582 } |
|
8583 |
|
8584 void |
|
8585 nsDocument::AsyncBlockOnload() |
|
8586 { |
|
8587 while (mAsyncOnloadBlockCount) { |
|
8588 --mAsyncOnloadBlockCount; |
|
8589 BlockOnload(); |
|
8590 } |
|
8591 } |
|
8592 |
|
8593 void |
|
8594 nsDocument::BlockOnload() |
|
8595 { |
|
8596 if (mDisplayDocument) { |
|
8597 mDisplayDocument->BlockOnload(); |
|
8598 return; |
|
8599 } |
|
8600 |
|
8601 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
|
8602 // -- it's not ours. |
|
8603 if (mOnloadBlockCount == 0 && mScriptGlobalObject) { |
|
8604 if (!nsContentUtils::IsSafeToRunScript()) { |
|
8605 // Because AddRequest may lead to OnStateChange calls in chrome, |
|
8606 // block onload only when there are no script blockers. |
|
8607 ++mAsyncOnloadBlockCount; |
|
8608 if (mAsyncOnloadBlockCount == 1) { |
|
8609 bool success = nsContentUtils::AddScriptRunner( |
|
8610 NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload)); |
|
8611 |
|
8612 // The script runner shouldn't fail to add. But if somebody broke |
|
8613 // something and it does, we'll thrash at 100% cpu forever. The best |
|
8614 // response is just to ignore the onload blocking request. See bug 579535. |
|
8615 if (!success) { |
|
8616 NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!"); |
|
8617 mAsyncOnloadBlockCount = 0; |
|
8618 } |
|
8619 } |
|
8620 return; |
|
8621 } |
|
8622 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
|
8623 if (loadGroup) { |
|
8624 loadGroup->AddRequest(mOnloadBlocker, nullptr); |
|
8625 } |
|
8626 } |
|
8627 ++mOnloadBlockCount; |
|
8628 } |
|
8629 |
|
8630 void |
|
8631 nsDocument::UnblockOnload(bool aFireSync) |
|
8632 { |
|
8633 if (mDisplayDocument) { |
|
8634 mDisplayDocument->UnblockOnload(aFireSync); |
|
8635 return; |
|
8636 } |
|
8637 |
|
8638 if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) { |
|
8639 NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call"); |
|
8640 return; |
|
8641 } |
|
8642 |
|
8643 --mOnloadBlockCount; |
|
8644 |
|
8645 if (mOnloadBlockCount == 0) { |
|
8646 if (mScriptGlobalObject) { |
|
8647 // Only manipulate the loadgroup in this case, because if mScriptGlobalObject |
|
8648 // is null, it's not ours. |
|
8649 if (aFireSync && mAsyncOnloadBlockCount == 0) { |
|
8650 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it |
|
8651 ++mOnloadBlockCount; |
|
8652 DoUnblockOnload(); |
|
8653 } else { |
|
8654 PostUnblockOnloadEvent(); |
|
8655 } |
|
8656 } else if (mIsBeingUsedAsImage) { |
|
8657 // To correctly unblock onload for a document that contains an SVG |
|
8658 // image, we need to know when all of the SVG document's resources are |
|
8659 // done loading, in a way comparable to |window.onload|. We fire this |
|
8660 // event to indicate that the SVG should be considered fully loaded. |
|
8661 // Because scripting is disabled on SVG-as-image documents, this event |
|
8662 // is not accessible to content authors. (See bug 837135.) |
|
8663 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
8664 new AsyncEventDispatcher(this, |
|
8665 NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), |
|
8666 false, |
|
8667 false); |
|
8668 asyncDispatcher->PostDOMEvent(); |
|
8669 } |
|
8670 } |
|
8671 } |
|
8672 |
|
8673 class nsUnblockOnloadEvent : public nsRunnable { |
|
8674 public: |
|
8675 nsUnblockOnloadEvent(nsDocument *doc) : mDoc(doc) {} |
|
8676 NS_IMETHOD Run() { |
|
8677 mDoc->DoUnblockOnload(); |
|
8678 return NS_OK; |
|
8679 } |
|
8680 private: |
|
8681 nsRefPtr<nsDocument> mDoc; |
|
8682 }; |
|
8683 |
|
8684 void |
|
8685 nsDocument::PostUnblockOnloadEvent() |
|
8686 { |
|
8687 nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this); |
|
8688 nsresult rv = NS_DispatchToCurrentThread(evt); |
|
8689 if (NS_SUCCEEDED(rv)) { |
|
8690 // Stabilize block count so we don't post more events while this one is up |
|
8691 ++mOnloadBlockCount; |
|
8692 } else { |
|
8693 NS_WARNING("failed to dispatch nsUnblockOnloadEvent"); |
|
8694 } |
|
8695 } |
|
8696 |
|
8697 void |
|
8698 nsDocument::DoUnblockOnload() |
|
8699 { |
|
8700 NS_PRECONDITION(!mDisplayDocument, |
|
8701 "Shouldn't get here for resource document"); |
|
8702 NS_PRECONDITION(mOnloadBlockCount != 0, |
|
8703 "Shouldn't have a count of zero here, since we stabilized in " |
|
8704 "PostUnblockOnloadEvent"); |
|
8705 |
|
8706 --mOnloadBlockCount; |
|
8707 |
|
8708 if (mOnloadBlockCount != 0) { |
|
8709 // We blocked again after the last unblock. Nothing to do here. We'll |
|
8710 // post a new event when we unblock again. |
|
8711 return; |
|
8712 } |
|
8713 |
|
8714 if (mAsyncOnloadBlockCount != 0) { |
|
8715 // We need to wait until the async onload block has been handled. |
|
8716 PostUnblockOnloadEvent(); |
|
8717 } |
|
8718 |
|
8719 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
|
8720 // -- it's not ours. |
|
8721 if (mScriptGlobalObject) { |
|
8722 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
|
8723 if (loadGroup) { |
|
8724 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); |
|
8725 } |
|
8726 } |
|
8727 } |
|
8728 |
|
8729 nsIContent* |
|
8730 nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const |
|
8731 { |
|
8732 for (nsIFrame* f = aFrame; f; |
|
8733 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { |
|
8734 nsIContent* content = f->GetContent(); |
|
8735 if (!content || content->IsInAnonymousSubtree()) |
|
8736 continue; |
|
8737 |
|
8738 if (content->OwnerDoc() == this) { |
|
8739 return content; |
|
8740 } |
|
8741 // We must be in a subdocument so jump directly to the root frame. |
|
8742 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to |
|
8743 // the containing document. |
|
8744 f = f->PresContext()->GetPresShell()->GetRootFrame(); |
|
8745 } |
|
8746 |
|
8747 return nullptr; |
|
8748 } |
|
8749 |
|
8750 void |
|
8751 nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget, |
|
8752 const nsAString& aType, |
|
8753 bool aPersisted) |
|
8754 { |
|
8755 if (aDispatchTarget) { |
|
8756 nsCOMPtr<nsIDOMEvent> event; |
|
8757 CreateEvent(NS_LITERAL_STRING("pagetransition"), getter_AddRefs(event)); |
|
8758 nsCOMPtr<nsIDOMPageTransitionEvent> ptEvent = do_QueryInterface(event); |
|
8759 if (ptEvent && NS_SUCCEEDED(ptEvent->InitPageTransitionEvent(aType, true, |
|
8760 true, |
|
8761 aPersisted))) { |
|
8762 event->SetTrusted(true); |
|
8763 event->SetTarget(this); |
|
8764 EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, |
|
8765 nullptr, nullptr); |
|
8766 } |
|
8767 } |
|
8768 } |
|
8769 |
|
8770 static bool |
|
8771 NotifyPageShow(nsIDocument* aDocument, void* aData) |
|
8772 { |
|
8773 const bool* aPersistedPtr = static_cast<const bool*>(aData); |
|
8774 aDocument->OnPageShow(*aPersistedPtr, nullptr); |
|
8775 return true; |
|
8776 } |
|
8777 |
|
8778 void |
|
8779 nsDocument::OnPageShow(bool aPersisted, |
|
8780 EventTarget* aDispatchStartTarget) |
|
8781 { |
|
8782 mVisible = true; |
|
8783 |
|
8784 EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
|
8785 EnumerateExternalResources(NotifyPageShow, &aPersisted); |
|
8786 |
|
8787 Element* root = GetRootElement(); |
|
8788 if (aPersisted && root) { |
|
8789 // Send out notifications that our <link> elements are attached. |
|
8790 nsRefPtr<nsContentList> links = NS_GetContentList(root, |
|
8791 kNameSpaceID_XHTML, |
|
8792 NS_LITERAL_STRING("link")); |
|
8793 |
|
8794 uint32_t linkCount = links->Length(true); |
|
8795 for (uint32_t i = 0; i < linkCount; ++i) { |
|
8796 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded(); |
|
8797 } |
|
8798 } |
|
8799 |
|
8800 // See nsIDocument |
|
8801 if (!aDispatchStartTarget) { |
|
8802 // Set mIsShowing before firing events, in case those event handlers |
|
8803 // move us around. |
|
8804 mIsShowing = true; |
|
8805 } |
|
8806 |
|
8807 if (mAnimationController) { |
|
8808 mAnimationController->OnPageShow(); |
|
8809 } |
|
8810 |
|
8811 if (aPersisted) { |
|
8812 SetImagesNeedAnimating(true); |
|
8813 } |
|
8814 |
|
8815 UpdateVisibilityState(); |
|
8816 |
|
8817 nsCOMPtr<EventTarget> target = aDispatchStartTarget; |
|
8818 if (!target) { |
|
8819 target = do_QueryInterface(GetWindow()); |
|
8820 } |
|
8821 |
|
8822 // Dispatch observer notification to notify observers page is shown. |
|
8823 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
8824 nsIPrincipal *principal = GetPrincipal(); |
|
8825 os->NotifyObservers(static_cast<nsIDocument*>(this), |
|
8826 nsContentUtils::IsSystemPrincipal(principal) ? |
|
8827 "chrome-page-shown" : |
|
8828 "content-page-shown", |
|
8829 nullptr); |
|
8830 |
|
8831 |
|
8832 DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted); |
|
8833 } |
|
8834 |
|
8835 static bool |
|
8836 NotifyPageHide(nsIDocument* aDocument, void* aData) |
|
8837 { |
|
8838 const bool* aPersistedPtr = static_cast<const bool*>(aData); |
|
8839 aDocument->OnPageHide(*aPersistedPtr, nullptr); |
|
8840 return true; |
|
8841 } |
|
8842 |
|
8843 static void |
|
8844 DispatchFullScreenChange(nsIDocument* aTarget) |
|
8845 { |
|
8846 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
8847 new AsyncEventDispatcher(aTarget, |
|
8848 NS_LITERAL_STRING("mozfullscreenchange"), |
|
8849 true, |
|
8850 false); |
|
8851 asyncDispatcher->PostDOMEvent(); |
|
8852 } |
|
8853 |
|
8854 void |
|
8855 nsDocument::OnPageHide(bool aPersisted, |
|
8856 EventTarget* aDispatchStartTarget) |
|
8857 { |
|
8858 // Send out notifications that our <link> elements are detached, |
|
8859 // but only if this is not a full unload. |
|
8860 Element* root = GetRootElement(); |
|
8861 if (aPersisted && root) { |
|
8862 nsRefPtr<nsContentList> links = NS_GetContentList(root, |
|
8863 kNameSpaceID_XHTML, |
|
8864 NS_LITERAL_STRING("link")); |
|
8865 |
|
8866 uint32_t linkCount = links->Length(true); |
|
8867 for (uint32_t i = 0; i < linkCount; ++i) { |
|
8868 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved(); |
|
8869 } |
|
8870 } |
|
8871 |
|
8872 // See nsIDocument |
|
8873 if (!aDispatchStartTarget) { |
|
8874 // Set mIsShowing before firing events, in case those event handlers |
|
8875 // move us around. |
|
8876 mIsShowing = false; |
|
8877 } |
|
8878 |
|
8879 if (mAnimationController) { |
|
8880 mAnimationController->OnPageHide(); |
|
8881 } |
|
8882 |
|
8883 if (aPersisted) { |
|
8884 SetImagesNeedAnimating(false); |
|
8885 } |
|
8886 |
|
8887 MozExitPointerLock(); |
|
8888 |
|
8889 // Now send out a PageHide event. |
|
8890 nsCOMPtr<EventTarget> target = aDispatchStartTarget; |
|
8891 if (!target) { |
|
8892 target = do_QueryInterface(GetWindow()); |
|
8893 } |
|
8894 |
|
8895 // Dispatch observer notification to notify observers page is hidden. |
|
8896 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
8897 nsIPrincipal *principal = GetPrincipal(); |
|
8898 os->NotifyObservers(static_cast<nsIDocument*>(this), |
|
8899 nsContentUtils::IsSystemPrincipal(principal) ? |
|
8900 "chrome-page-hidden" : |
|
8901 "content-page-hidden", |
|
8902 nullptr); |
|
8903 |
|
8904 DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted); |
|
8905 |
|
8906 mVisible = false; |
|
8907 |
|
8908 UpdateVisibilityState(); |
|
8909 |
|
8910 EnumerateExternalResources(NotifyPageHide, &aPersisted); |
|
8911 EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
|
8912 |
|
8913 if (IsFullScreenDoc()) { |
|
8914 // If this document was fullscreen, we should exit fullscreen in this |
|
8915 // doctree branch. This ensures that if the user navigates while in |
|
8916 // fullscreen mode we don't leave its still visible ancestor documents |
|
8917 // in fullscreen mode. So exit fullscreen in the document's fullscreen |
|
8918 // root document, as this will exit fullscreen in all the root's |
|
8919 // descendant documents. Note that documents are removed from the |
|
8920 // doctree by the time OnPageHide() is called, so we must store a |
|
8921 // reference to the root (in nsDocument::mFullscreenRoot) since we can't |
|
8922 // just traverse the doctree to get the root. |
|
8923 nsIDocument::ExitFullscreen(this, /* async */ false); |
|
8924 |
|
8925 // Since the document is removed from the doctree before OnPageHide() is |
|
8926 // called, ExitFullscreen() can't traverse from the root down to *this* |
|
8927 // document, so we must manually call CleanupFullscreenState() below too. |
|
8928 // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot, |
|
8929 // so we *must* call it after ExitFullscreen(), not before. |
|
8930 // OnPageHide() is called in every hidden (i.e. descendant) document, |
|
8931 // so calling CleanupFullscreenState() here will ensure all hidden |
|
8932 // documents have their fullscreen state reset. |
|
8933 CleanupFullscreenState(); |
|
8934 |
|
8935 // If anyone was listening to this document's state, advertizing the state |
|
8936 // change would be the least of the politeness. |
|
8937 DispatchFullScreenChange(this); |
|
8938 } |
|
8939 } |
|
8940 |
|
8941 void |
|
8942 nsDocument::WillDispatchMutationEvent(nsINode* aTarget) |
|
8943 { |
|
8944 NS_ASSERTION(mSubtreeModifiedDepth != 0 || |
|
8945 mSubtreeModifiedTargets.Count() == 0, |
|
8946 "mSubtreeModifiedTargets not cleared after dispatching?"); |
|
8947 ++mSubtreeModifiedDepth; |
|
8948 if (aTarget) { |
|
8949 // MayDispatchMutationEvent is often called just before this method, |
|
8950 // so it has already appended the node to mSubtreeModifiedTargets. |
|
8951 int32_t count = mSubtreeModifiedTargets.Count(); |
|
8952 if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) { |
|
8953 mSubtreeModifiedTargets.AppendObject(aTarget); |
|
8954 } |
|
8955 } |
|
8956 } |
|
8957 |
|
8958 void |
|
8959 nsDocument::MutationEventDispatched(nsINode* aTarget) |
|
8960 { |
|
8961 --mSubtreeModifiedDepth; |
|
8962 if (mSubtreeModifiedDepth == 0) { |
|
8963 int32_t count = mSubtreeModifiedTargets.Count(); |
|
8964 if (!count) { |
|
8965 return; |
|
8966 } |
|
8967 |
|
8968 nsCOMPtr<nsPIDOMWindow> window; |
|
8969 window = do_QueryInterface(GetWindow()); |
|
8970 if (window && |
|
8971 !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) { |
|
8972 mSubtreeModifiedTargets.Clear(); |
|
8973 return; |
|
8974 } |
|
8975 |
|
8976 nsCOMArray<nsINode> realTargets; |
|
8977 for (int32_t i = 0; i < count; ++i) { |
|
8978 nsINode* possibleTarget = mSubtreeModifiedTargets[i]; |
|
8979 nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget); |
|
8980 if (content && content->ChromeOnlyAccess()) { |
|
8981 continue; |
|
8982 } |
|
8983 |
|
8984 nsINode* commonAncestor = nullptr; |
|
8985 int32_t realTargetCount = realTargets.Count(); |
|
8986 for (int32_t j = 0; j < realTargetCount; ++j) { |
|
8987 commonAncestor = |
|
8988 nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]); |
|
8989 if (commonAncestor) { |
|
8990 realTargets.ReplaceObjectAt(commonAncestor, j); |
|
8991 break; |
|
8992 } |
|
8993 } |
|
8994 if (!commonAncestor) { |
|
8995 realTargets.AppendObject(possibleTarget); |
|
8996 } |
|
8997 } |
|
8998 |
|
8999 mSubtreeModifiedTargets.Clear(); |
|
9000 |
|
9001 int32_t realTargetCount = realTargets.Count(); |
|
9002 for (int32_t k = 0; k < realTargetCount; ++k) { |
|
9003 InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED); |
|
9004 (new AsyncEventDispatcher(realTargets[k], mutation))-> |
|
9005 RunDOMEventWhenSafe(); |
|
9006 } |
|
9007 } |
|
9008 } |
|
9009 |
|
9010 void |
|
9011 nsDocument::AddStyleRelevantLink(Link* aLink) |
|
9012 { |
|
9013 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!"); |
|
9014 #ifdef DEBUG |
|
9015 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink); |
|
9016 NS_ASSERTION(!entry, "Document already knows about this Link!"); |
|
9017 mStyledLinksCleared = false; |
|
9018 #endif |
|
9019 (void)mStyledLinks.PutEntry(aLink); |
|
9020 } |
|
9021 |
|
9022 void |
|
9023 nsDocument::ForgetLink(Link* aLink) |
|
9024 { |
|
9025 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!"); |
|
9026 #ifdef DEBUG |
|
9027 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink); |
|
9028 NS_ASSERTION(entry || mStyledLinksCleared, |
|
9029 "Document knows nothing about this Link!"); |
|
9030 #endif |
|
9031 (void)mStyledLinks.RemoveEntry(aLink); |
|
9032 } |
|
9033 |
|
9034 void |
|
9035 nsDocument::DestroyElementMaps() |
|
9036 { |
|
9037 #ifdef DEBUG |
|
9038 mStyledLinksCleared = true; |
|
9039 #endif |
|
9040 mStyledLinks.Clear(); |
|
9041 mIdentifierMap.Clear(); |
|
9042 ++mExpandoAndGeneration.generation; |
|
9043 } |
|
9044 |
|
9045 static |
|
9046 PLDHashOperator |
|
9047 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray) |
|
9048 { |
|
9049 LinkArray* array = static_cast<LinkArray*>(aArray); |
|
9050 (void)array->AppendElement(aEntry->GetKey()); |
|
9051 return PL_DHASH_NEXT; |
|
9052 } |
|
9053 |
|
9054 void |
|
9055 nsDocument::RefreshLinkHrefs() |
|
9056 { |
|
9057 // Get a list of all links we know about. We will reset them, which will |
|
9058 // remove them from the document, so we need a copy of what is in the |
|
9059 // hashtable. |
|
9060 LinkArray linksToNotify(mStyledLinks.Count()); |
|
9061 (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify); |
|
9062 |
|
9063 // Reset all of our styled links. |
|
9064 nsAutoScriptBlocker scriptBlocker; |
|
9065 for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) { |
|
9066 linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref()); |
|
9067 } |
|
9068 } |
|
9069 |
|
9070 nsresult |
|
9071 nsDocument::CloneDocHelper(nsDocument* clone) const |
|
9072 { |
|
9073 clone->mIsStaticDocument = mCreatingStaticClone; |
|
9074 |
|
9075 // Init document |
|
9076 nsresult rv = clone->Init(); |
|
9077 NS_ENSURE_SUCCESS(rv, rv); |
|
9078 |
|
9079 // Set URI/principal |
|
9080 clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI()); |
|
9081 clone->SetChromeXHRDocURI(mChromeXHRDocURI); |
|
9082 // Must set the principal first, since SetBaseURI checks it. |
|
9083 clone->SetPrincipal(NodePrincipal()); |
|
9084 clone->mDocumentBaseURI = mDocumentBaseURI; |
|
9085 clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI); |
|
9086 |
|
9087 if (mCreatingStaticClone) { |
|
9088 nsCOMPtr<nsILoadGroup> loadGroup; |
|
9089 |
|
9090 // |mDocumentContainer| is the container of the document that is being |
|
9091 // created and not the original container. See CreateStaticClone function(). |
|
9092 nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer); |
|
9093 if (docLoader) { |
|
9094 docLoader->GetLoadGroup(getter_AddRefs(loadGroup)); |
|
9095 } |
|
9096 nsCOMPtr<nsIChannel> channel = GetChannel(); |
|
9097 if (channel && loadGroup) { |
|
9098 clone->Reset(channel, loadGroup); |
|
9099 } else { |
|
9100 nsIURI* uri = static_cast<const nsIDocument*>(this)->GetDocumentURI(); |
|
9101 if (uri) { |
|
9102 clone->ResetToURI(uri, loadGroup, NodePrincipal()); |
|
9103 } |
|
9104 } |
|
9105 clone->SetContainer(mDocumentContainer); |
|
9106 } |
|
9107 |
|
9108 // Set scripting object |
|
9109 bool hasHadScriptObject = true; |
|
9110 nsIScriptGlobalObject* scriptObject = |
|
9111 GetScriptHandlingObject(hasHadScriptObject); |
|
9112 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject); |
|
9113 if (scriptObject) { |
|
9114 clone->SetScriptHandlingObject(scriptObject); |
|
9115 } else { |
|
9116 clone->SetScopeObject(GetScopeObject()); |
|
9117 } |
|
9118 // Make the clone a data document |
|
9119 clone->SetLoadedAsData(true); |
|
9120 |
|
9121 // Misc state |
|
9122 |
|
9123 // State from nsIDocument |
|
9124 clone->mCharacterSet = mCharacterSet; |
|
9125 clone->mCharacterSetSource = mCharacterSetSource; |
|
9126 clone->mCompatMode = mCompatMode; |
|
9127 clone->mBidiOptions = mBidiOptions; |
|
9128 clone->mContentLanguage = mContentLanguage; |
|
9129 clone->SetContentTypeInternal(GetContentTypeInternal()); |
|
9130 clone->mSecurityInfo = mSecurityInfo; |
|
9131 |
|
9132 // State from nsDocument |
|
9133 clone->mIsRegularHTML = mIsRegularHTML; |
|
9134 clone->mXMLDeclarationBits = mXMLDeclarationBits; |
|
9135 clone->mBaseTarget = mBaseTarget; |
|
9136 return NS_OK; |
|
9137 } |
|
9138 |
|
9139 void |
|
9140 nsDocument::SetReadyStateInternal(ReadyState rs) |
|
9141 { |
|
9142 mReadyState = rs; |
|
9143 if (rs == READYSTATE_UNINITIALIZED) { |
|
9144 // Transition back to uninitialized happens only to keep assertions happy |
|
9145 // right before readyState transitions to something else. Make this |
|
9146 // transition undetectable by Web content. |
|
9147 return; |
|
9148 } |
|
9149 if (mTiming) { |
|
9150 switch (rs) { |
|
9151 case READYSTATE_LOADING: |
|
9152 mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI()); |
|
9153 break; |
|
9154 case READYSTATE_INTERACTIVE: |
|
9155 mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI()); |
|
9156 break; |
|
9157 case READYSTATE_COMPLETE: |
|
9158 mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI()); |
|
9159 break; |
|
9160 default: |
|
9161 NS_WARNING("Unexpected ReadyState value"); |
|
9162 break; |
|
9163 } |
|
9164 } |
|
9165 // At the time of loading start, we don't have timing object, record time. |
|
9166 if (READYSTATE_LOADING == rs) { |
|
9167 mLoadingTimeStamp = mozilla::TimeStamp::Now(); |
|
9168 } |
|
9169 |
|
9170 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
9171 new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"), |
|
9172 false, false); |
|
9173 asyncDispatcher->RunDOMEventWhenSafe(); |
|
9174 } |
|
9175 |
|
9176 NS_IMETHODIMP |
|
9177 nsDocument::GetReadyState(nsAString& aReadyState) |
|
9178 { |
|
9179 nsIDocument::GetReadyState(aReadyState); |
|
9180 return NS_OK; |
|
9181 } |
|
9182 |
|
9183 void |
|
9184 nsIDocument::GetReadyState(nsAString& aReadyState) const |
|
9185 { |
|
9186 switch(mReadyState) { |
|
9187 case READYSTATE_LOADING : |
|
9188 aReadyState.Assign(NS_LITERAL_STRING("loading")); |
|
9189 break; |
|
9190 case READYSTATE_INTERACTIVE : |
|
9191 aReadyState.Assign(NS_LITERAL_STRING("interactive")); |
|
9192 break; |
|
9193 case READYSTATE_COMPLETE : |
|
9194 aReadyState.Assign(NS_LITERAL_STRING("complete")); |
|
9195 break; |
|
9196 default: |
|
9197 aReadyState.Assign(NS_LITERAL_STRING("uninitialized")); |
|
9198 } |
|
9199 } |
|
9200 |
|
9201 namespace { |
|
9202 |
|
9203 struct SuppressArgs |
|
9204 { |
|
9205 nsIDocument::SuppressionType mWhat; |
|
9206 uint32_t mIncrease; |
|
9207 }; |
|
9208 |
|
9209 } |
|
9210 |
|
9211 static bool |
|
9212 SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData) |
|
9213 { |
|
9214 SuppressArgs* args = static_cast<SuppressArgs*>(aData); |
|
9215 aDocument->SuppressEventHandling(args->mWhat, args->mIncrease); |
|
9216 return true; |
|
9217 } |
|
9218 |
|
9219 void |
|
9220 nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat, |
|
9221 uint32_t aIncrease) |
|
9222 { |
|
9223 if (mEventsSuppressed == 0 && mAnimationsPaused == 0 && |
|
9224 aIncrease != 0 && mPresShell && mScriptGlobalObject) { |
|
9225 RevokeAnimationFrameNotifications(); |
|
9226 } |
|
9227 |
|
9228 if (aWhat == eAnimationsOnly) { |
|
9229 mAnimationsPaused += aIncrease; |
|
9230 } else { |
|
9231 mEventsSuppressed += aIncrease; |
|
9232 } |
|
9233 |
|
9234 SuppressArgs args = { aWhat, aIncrease }; |
|
9235 EnumerateSubDocuments(SuppressEventHandlingInDocument, &args); |
|
9236 } |
|
9237 |
|
9238 static void |
|
9239 FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments, |
|
9240 bool aFireEvents) |
|
9241 { |
|
9242 nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
|
9243 if (!fm) |
|
9244 return; |
|
9245 |
|
9246 for (uint32_t i = 0; i < aDocuments.Length(); ++i) { |
|
9247 // NB: Don't bother trying to fire delayed events on documents that were |
|
9248 // closed before this event ran. |
|
9249 if (!aDocuments[i]->EventHandlingSuppressed()) { |
|
9250 fm->FireDelayedEvents(aDocuments[i]); |
|
9251 nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell(); |
|
9252 if (shell) { |
|
9253 // Only fire events for active documents. |
|
9254 bool fire = aFireEvents && |
|
9255 aDocuments[i]->GetInnerWindow() && |
|
9256 aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow(); |
|
9257 shell->FireOrClearDelayedEvents(fire); |
|
9258 } |
|
9259 } |
|
9260 } |
|
9261 } |
|
9262 |
|
9263 void |
|
9264 nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr) |
|
9265 { |
|
9266 // Early exit if the img is already present in the img-cache |
|
9267 // which indicates that the "real" load has already started and |
|
9268 // that we shouldn't preload it. |
|
9269 int16_t blockingStatus; |
|
9270 if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) || |
|
9271 !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this), |
|
9272 this, NodePrincipal(), &blockingStatus)) { |
|
9273 return; |
|
9274 } |
|
9275 |
|
9276 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; |
|
9277 switch (Element::StringToCORSMode(aCrossOriginAttr)) { |
|
9278 case CORS_NONE: |
|
9279 // Nothing to do |
|
9280 break; |
|
9281 case CORS_ANONYMOUS: |
|
9282 loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS; |
|
9283 break; |
|
9284 case CORS_USE_CREDENTIALS: |
|
9285 loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS; |
|
9286 break; |
|
9287 default: |
|
9288 MOZ_CRASH("Unknown CORS mode!"); |
|
9289 } |
|
9290 |
|
9291 // Image not in cache - trigger preload |
|
9292 nsRefPtr<imgRequestProxy> request; |
|
9293 nsresult rv = |
|
9294 nsContentUtils::LoadImage(uri, |
|
9295 this, |
|
9296 NodePrincipal(), |
|
9297 mDocumentURI, // uri of document used as referrer |
|
9298 nullptr, // no observer |
|
9299 loadFlags, |
|
9300 NS_LITERAL_STRING("img"), |
|
9301 getter_AddRefs(request)); |
|
9302 |
|
9303 // Pin image-reference to avoid evicting it from the img-cache before |
|
9304 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and |
|
9305 // unlink |
|
9306 if (NS_SUCCEEDED(rv)) { |
|
9307 mPreloadingImages.AppendObject(request); |
|
9308 } |
|
9309 } |
|
9310 |
|
9311 EventStates |
|
9312 nsDocument::GetDocumentState() |
|
9313 { |
|
9314 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) { |
|
9315 if (IsDocumentRightToLeft()) { |
|
9316 mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE; |
|
9317 } |
|
9318 mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE; |
|
9319 } |
|
9320 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { |
|
9321 nsIPresShell* shell = GetShell(); |
|
9322 if (shell && shell->GetPresContext() && |
|
9323 shell->GetPresContext()->IsTopLevelWindowInactive()) { |
|
9324 mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; |
|
9325 } |
|
9326 mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; |
|
9327 } |
|
9328 return mDocumentState; |
|
9329 } |
|
9330 |
|
9331 namespace { |
|
9332 |
|
9333 /** |
|
9334 * Stub for LoadSheet(), since all we want is to get the sheet into |
|
9335 * the CSSLoader's style cache |
|
9336 */ |
|
9337 class StubCSSLoaderObserver MOZ_FINAL : public nsICSSLoaderObserver { |
|
9338 public: |
|
9339 NS_IMETHOD |
|
9340 StyleSheetLoaded(nsCSSStyleSheet*, bool, nsresult) |
|
9341 { |
|
9342 return NS_OK; |
|
9343 } |
|
9344 NS_DECL_ISUPPORTS |
|
9345 }; |
|
9346 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver) |
|
9347 |
|
9348 } |
|
9349 |
|
9350 void |
|
9351 nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset, |
|
9352 const nsAString& aCrossOriginAttr) |
|
9353 { |
|
9354 // The CSSLoader will retain this object after we return. |
|
9355 nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver(); |
|
9356 |
|
9357 // Charset names are always ASCII. |
|
9358 CSSLoader()->LoadSheet(uri, NodePrincipal(), |
|
9359 NS_LossyConvertUTF16toASCII(charset), |
|
9360 obs, |
|
9361 Element::StringToCORSMode(aCrossOriginAttr)); |
|
9362 } |
|
9363 |
|
9364 nsresult |
|
9365 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet, |
|
9366 nsCSSStyleSheet** sheet) |
|
9367 { |
|
9368 return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet); |
|
9369 } |
|
9370 |
|
9371 class nsDelayedEventDispatcher : public nsRunnable |
|
9372 { |
|
9373 public: |
|
9374 nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments) |
|
9375 { |
|
9376 mDocuments.SwapElements(aDocuments); |
|
9377 } |
|
9378 virtual ~nsDelayedEventDispatcher() {} |
|
9379 |
|
9380 NS_IMETHOD Run() |
|
9381 { |
|
9382 FireOrClearDelayedEvents(mDocuments, true); |
|
9383 return NS_OK; |
|
9384 } |
|
9385 |
|
9386 private: |
|
9387 nsTArray<nsCOMPtr<nsIDocument> > mDocuments; |
|
9388 }; |
|
9389 |
|
9390 namespace { |
|
9391 |
|
9392 struct UnsuppressArgs |
|
9393 { |
|
9394 UnsuppressArgs(nsIDocument::SuppressionType aWhat) |
|
9395 : mWhat(aWhat) |
|
9396 { |
|
9397 } |
|
9398 |
|
9399 nsIDocument::SuppressionType mWhat; |
|
9400 nsTArray<nsCOMPtr<nsIDocument>> mDocs; |
|
9401 }; |
|
9402 |
|
9403 } |
|
9404 |
|
9405 static bool |
|
9406 GetAndUnsuppressSubDocuments(nsIDocument* aDocument, |
|
9407 void* aData) |
|
9408 { |
|
9409 UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData); |
|
9410 if (args->mWhat != nsIDocument::eAnimationsOnly && |
|
9411 aDocument->EventHandlingSuppressed() > 0) { |
|
9412 static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression(); |
|
9413 } else if (args->mWhat == nsIDocument::eAnimationsOnly && |
|
9414 aDocument->AnimationsPaused()) { |
|
9415 static_cast<nsDocument*>(aDocument)->ResumeAnimations(); |
|
9416 } |
|
9417 |
|
9418 if (args->mWhat != nsIDocument::eAnimationsOnly) { |
|
9419 // No need to remember documents if we only care about animation frames. |
|
9420 args->mDocs.AppendElement(aDocument); |
|
9421 } |
|
9422 |
|
9423 aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData); |
|
9424 return true; |
|
9425 } |
|
9426 |
|
9427 void |
|
9428 nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat, |
|
9429 bool aFireEvents) |
|
9430 { |
|
9431 UnsuppressArgs args(aWhat); |
|
9432 GetAndUnsuppressSubDocuments(this, &args); |
|
9433 |
|
9434 if (aWhat == nsIDocument::eAnimationsOnly) { |
|
9435 // No need to fire events if we only care about animations here. |
|
9436 return; |
|
9437 } |
|
9438 |
|
9439 if (aFireEvents) { |
|
9440 NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs)); |
|
9441 } else { |
|
9442 FireOrClearDelayedEvents(args.mDocs, false); |
|
9443 } |
|
9444 } |
|
9445 |
|
9446 nsISupports* |
|
9447 nsDocument::GetCurrentContentSink() |
|
9448 { |
|
9449 return mParser ? mParser->GetContentSink() : nullptr; |
|
9450 } |
|
9451 |
|
9452 nsIDocument* |
|
9453 nsDocument::GetTemplateContentsOwner() |
|
9454 { |
|
9455 if (!mTemplateContentsOwner) { |
|
9456 bool hasHadScriptObject = true; |
|
9457 nsIScriptGlobalObject* scriptObject = |
|
9458 GetScriptHandlingObject(hasHadScriptObject); |
|
9459 NS_ENSURE_TRUE(scriptObject || !hasHadScriptObject, nullptr); |
|
9460 |
|
9461 nsCOMPtr<nsIDOMDocument> domDocument; |
|
9462 nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument), |
|
9463 EmptyString(), // aNamespaceURI |
|
9464 EmptyString(), // aQualifiedName |
|
9465 nullptr, // aDoctype |
|
9466 nsIDocument::GetDocumentURI(), |
|
9467 nsIDocument::GetDocBaseURI(), |
|
9468 NodePrincipal(), |
|
9469 true, // aLoadedAsData |
|
9470 scriptObject, // aEventObject |
|
9471 DocumentFlavorHTML); |
|
9472 NS_ENSURE_SUCCESS(rv, nullptr); |
|
9473 |
|
9474 mTemplateContentsOwner = do_QueryInterface(domDocument); |
|
9475 NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr); |
|
9476 |
|
9477 mTemplateContentsOwner->SetScriptHandlingObject(scriptObject); |
|
9478 |
|
9479 // Set |doc| as the template contents owner of itself so that |
|
9480 // |doc| is the template contents owner of template elements created |
|
9481 // by |doc|. |
|
9482 nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get()); |
|
9483 doc->mTemplateContentsOwner = doc; |
|
9484 } |
|
9485 |
|
9486 return mTemplateContentsOwner; |
|
9487 } |
|
9488 |
|
9489 void |
|
9490 nsDocument::RegisterHostObjectUri(const nsACString& aUri) |
|
9491 { |
|
9492 mHostObjectURIs.AppendElement(aUri); |
|
9493 } |
|
9494 |
|
9495 void |
|
9496 nsDocument::UnregisterHostObjectUri(const nsACString& aUri) |
|
9497 { |
|
9498 mHostObjectURIs.RemoveElement(aUri); |
|
9499 } |
|
9500 |
|
9501 void |
|
9502 nsDocument::SetScrollToRef(nsIURI *aDocumentURI) |
|
9503 { |
|
9504 if (!aDocumentURI) { |
|
9505 return; |
|
9506 } |
|
9507 |
|
9508 nsAutoCString ref; |
|
9509 |
|
9510 // Since all URI's that pass through here aren't URL's we can't |
|
9511 // rely on the nsIURI implementation for providing a way for |
|
9512 // finding the 'ref' part of the URI, we'll haveto revert to |
|
9513 // string routines for finding the data past '#' |
|
9514 |
|
9515 aDocumentURI->GetSpec(ref); |
|
9516 |
|
9517 nsReadingIterator<char> start, end; |
|
9518 |
|
9519 ref.BeginReading(start); |
|
9520 ref.EndReading(end); |
|
9521 |
|
9522 if (FindCharInReadable('#', start, end)) { |
|
9523 ++start; // Skip over the '#' |
|
9524 |
|
9525 mScrollToRef = Substring(start, end); |
|
9526 } |
|
9527 } |
|
9528 |
|
9529 void |
|
9530 nsDocument::ScrollToRef() |
|
9531 { |
|
9532 if (mScrolledToRefAlready) { |
|
9533 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
9534 if (shell) { |
|
9535 shell->ScrollToAnchor(); |
|
9536 } |
|
9537 return; |
|
9538 } |
|
9539 |
|
9540 if (mScrollToRef.IsEmpty()) { |
|
9541 return; |
|
9542 } |
|
9543 |
|
9544 char* tmpstr = ToNewCString(mScrollToRef); |
|
9545 if (!tmpstr) { |
|
9546 return; |
|
9547 } |
|
9548 |
|
9549 nsUnescape(tmpstr); |
|
9550 nsAutoCString unescapedRef; |
|
9551 unescapedRef.Assign(tmpstr); |
|
9552 nsMemory::Free(tmpstr); |
|
9553 |
|
9554 nsresult rv = NS_ERROR_FAILURE; |
|
9555 // We assume that the bytes are in UTF-8, as it says in the spec: |
|
9556 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1 |
|
9557 NS_ConvertUTF8toUTF16 ref(unescapedRef); |
|
9558 |
|
9559 nsCOMPtr<nsIPresShell> shell = GetShell(); |
|
9560 if (shell) { |
|
9561 // Check an empty string which might be caused by the UTF-8 conversion |
|
9562 if (!ref.IsEmpty()) { |
|
9563 // Note that GoToAnchor will handle flushing layout as needed. |
|
9564 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef); |
|
9565 } else { |
|
9566 rv = NS_ERROR_FAILURE; |
|
9567 } |
|
9568 |
|
9569 // If UTF-8 URI failed then try to assume the string as a |
|
9570 // document's charset. |
|
9571 |
|
9572 if (NS_FAILED(rv)) { |
|
9573 const nsACString &docCharset = GetDocumentCharacterSet(); |
|
9574 |
|
9575 rv = nsContentUtils::ConvertStringFromEncoding(docCharset, |
|
9576 unescapedRef, |
|
9577 ref); |
|
9578 |
|
9579 if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) { |
|
9580 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef); |
|
9581 } |
|
9582 } |
|
9583 if (NS_SUCCEEDED(rv)) { |
|
9584 mScrolledToRefAlready = true; |
|
9585 } |
|
9586 } |
|
9587 } |
|
9588 |
|
9589 void |
|
9590 nsDocument::ResetScrolledToRefAlready() |
|
9591 { |
|
9592 mScrolledToRefAlready = false; |
|
9593 } |
|
9594 |
|
9595 void |
|
9596 nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue) |
|
9597 { |
|
9598 mChangeScrollPosWhenScrollingToRef = aValue; |
|
9599 } |
|
9600 |
|
9601 void |
|
9602 nsIDocument::RegisterFreezableElement(nsIContent* aContent) |
|
9603 { |
|
9604 if (!mFreezableElements) { |
|
9605 mFreezableElements = new nsTHashtable<nsPtrHashKey<nsIContent> >(); |
|
9606 if (!mFreezableElements) |
|
9607 return; |
|
9608 } |
|
9609 mFreezableElements->PutEntry(aContent); |
|
9610 } |
|
9611 |
|
9612 bool |
|
9613 nsIDocument::UnregisterFreezableElement(nsIContent* aContent) |
|
9614 { |
|
9615 if (!mFreezableElements) |
|
9616 return false; |
|
9617 if (!mFreezableElements->GetEntry(aContent)) |
|
9618 return false; |
|
9619 mFreezableElements->RemoveEntry(aContent); |
|
9620 return true; |
|
9621 } |
|
9622 |
|
9623 struct EnumerateFreezablesData { |
|
9624 nsIDocument::FreezableElementEnumerator mEnumerator; |
|
9625 void* mData; |
|
9626 }; |
|
9627 |
|
9628 static PLDHashOperator |
|
9629 EnumerateFreezables(nsPtrHashKey<nsIContent>* aEntry, void* aData) |
|
9630 { |
|
9631 EnumerateFreezablesData* data = static_cast<EnumerateFreezablesData*>(aData); |
|
9632 data->mEnumerator(aEntry->GetKey(), data->mData); |
|
9633 return PL_DHASH_NEXT; |
|
9634 } |
|
9635 |
|
9636 void |
|
9637 nsIDocument::EnumerateFreezableElements(FreezableElementEnumerator aEnumerator, |
|
9638 void* aData) |
|
9639 { |
|
9640 if (!mFreezableElements) |
|
9641 return; |
|
9642 EnumerateFreezablesData data = { aEnumerator, aData }; |
|
9643 mFreezableElements->EnumerateEntries(EnumerateFreezables, &data); |
|
9644 } |
|
9645 |
|
9646 void |
|
9647 nsIDocument::RegisterPendingLinkUpdate(Link* aLink) |
|
9648 { |
|
9649 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden); |
|
9650 mLinksToUpdate.PutEntry(aLink); |
|
9651 mHasLinksToUpdate = true; |
|
9652 } |
|
9653 |
|
9654 void |
|
9655 nsIDocument::UnregisterPendingLinkUpdate(Link* aLink) |
|
9656 { |
|
9657 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden); |
|
9658 if (!mHasLinksToUpdate) |
|
9659 return; |
|
9660 |
|
9661 mLinksToUpdate.RemoveEntry(aLink); |
|
9662 } |
|
9663 |
|
9664 static PLDHashOperator |
|
9665 EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData) |
|
9666 { |
|
9667 aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState()); |
|
9668 return PL_DHASH_NEXT; |
|
9669 } |
|
9670 |
|
9671 void |
|
9672 nsIDocument::FlushPendingLinkUpdates() |
|
9673 { |
|
9674 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden); |
|
9675 if (!mHasLinksToUpdate) |
|
9676 return; |
|
9677 |
|
9678 #ifdef DEBUG |
|
9679 AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden); |
|
9680 mIsLinkUpdateRegistrationsForbidden = true; |
|
9681 #endif |
|
9682 mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nullptr); |
|
9683 mLinksToUpdate.Clear(); |
|
9684 mHasLinksToUpdate = false; |
|
9685 } |
|
9686 |
|
9687 already_AddRefed<nsIDocument> |
|
9688 nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer) |
|
9689 { |
|
9690 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(this); |
|
9691 NS_ENSURE_TRUE(domDoc, nullptr); |
|
9692 mCreatingStaticClone = true; |
|
9693 |
|
9694 // Make document use different container during cloning. |
|
9695 nsRefPtr<nsDocShell> originalShell = mDocumentContainer.get(); |
|
9696 SetContainer(static_cast<nsDocShell*>(aCloneContainer)); |
|
9697 nsCOMPtr<nsIDOMNode> clonedNode; |
|
9698 nsresult rv = domDoc->CloneNode(true, 1, getter_AddRefs(clonedNode)); |
|
9699 SetContainer(originalShell); |
|
9700 |
|
9701 nsCOMPtr<nsIDocument> clonedDoc; |
|
9702 if (NS_SUCCEEDED(rv)) { |
|
9703 clonedDoc = do_QueryInterface(clonedNode); |
|
9704 if (clonedDoc) { |
|
9705 if (IsStaticDocument()) { |
|
9706 clonedDoc->mOriginalDocument = mOriginalDocument; |
|
9707 } else { |
|
9708 clonedDoc->mOriginalDocument = this; |
|
9709 } |
|
9710 int32_t sheetsCount = GetNumberOfStyleSheets(); |
|
9711 for (int32_t i = 0; i < sheetsCount; ++i) { |
|
9712 nsRefPtr<nsCSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i)); |
|
9713 if (sheet) { |
|
9714 if (sheet->IsApplicable()) { |
|
9715 nsRefPtr<nsCSSStyleSheet> clonedSheet = |
|
9716 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr); |
|
9717 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!"); |
|
9718 if (clonedSheet) { |
|
9719 clonedDoc->AddStyleSheet(clonedSheet); |
|
9720 } |
|
9721 } |
|
9722 } |
|
9723 } |
|
9724 |
|
9725 sheetsCount = GetNumberOfCatalogStyleSheets(); |
|
9726 for (int32_t i = 0; i < sheetsCount; ++i) { |
|
9727 nsRefPtr<nsCSSStyleSheet> sheet = |
|
9728 do_QueryObject(GetCatalogStyleSheetAt(i)); |
|
9729 if (sheet) { |
|
9730 if (sheet->IsApplicable()) { |
|
9731 nsRefPtr<nsCSSStyleSheet> clonedSheet = |
|
9732 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr); |
|
9733 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!"); |
|
9734 if (clonedSheet) { |
|
9735 clonedDoc->AddCatalogStyleSheet(clonedSheet); |
|
9736 } |
|
9737 } |
|
9738 } |
|
9739 } |
|
9740 } |
|
9741 } |
|
9742 mCreatingStaticClone = false; |
|
9743 return clonedDoc.forget(); |
|
9744 } |
|
9745 |
|
9746 nsresult |
|
9747 nsIDocument::ScheduleFrameRequestCallback(const FrameRequestCallbackHolder& aCallback, |
|
9748 int32_t *aHandle) |
|
9749 { |
|
9750 if (mFrameRequestCallbackCounter == INT32_MAX) { |
|
9751 // Can't increment without overflowing; bail out |
|
9752 return NS_ERROR_NOT_AVAILABLE; |
|
9753 } |
|
9754 int32_t newHandle = ++mFrameRequestCallbackCounter; |
|
9755 |
|
9756 bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty(); |
|
9757 DebugOnly<FrameRequest*> request = |
|
9758 mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); |
|
9759 NS_ASSERTION(request, "This is supposed to be infallible!"); |
|
9760 if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) { |
|
9761 mPresShell->GetPresContext()->RefreshDriver()-> |
|
9762 ScheduleFrameRequestCallbacks(this); |
|
9763 } |
|
9764 |
|
9765 *aHandle = newHandle; |
|
9766 return NS_OK; |
|
9767 } |
|
9768 |
|
9769 void |
|
9770 nsIDocument::CancelFrameRequestCallback(int32_t aHandle) |
|
9771 { |
|
9772 // mFrameRequestCallbacks is stored sorted by handle |
|
9773 if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) && |
|
9774 mFrameRequestCallbacks.IsEmpty() && |
|
9775 mPresShell && IsEventHandlingEnabled()) { |
|
9776 mPresShell->GetPresContext()->RefreshDriver()-> |
|
9777 RevokeFrameRequestCallbacks(this); |
|
9778 } |
|
9779 } |
|
9780 |
|
9781 nsresult |
|
9782 nsDocument::GetStateObject(nsIVariant** aState) |
|
9783 { |
|
9784 // Get the document's current state object. This is the object backing both |
|
9785 // history.state and popStateEvent.state. |
|
9786 // |
|
9787 // mStateObjectContainer may be null; this just means that there's no |
|
9788 // current state object. |
|
9789 |
|
9790 nsCOMPtr<nsIVariant> stateObj; |
|
9791 if (!mStateObjectCached && mStateObjectContainer) { |
|
9792 AutoJSContext cx; |
|
9793 nsIGlobalObject* sgo = GetScopeObject(); |
|
9794 NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); |
|
9795 JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); |
|
9796 NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED); |
|
9797 JSAutoCompartment ac(cx, global); |
|
9798 |
|
9799 mStateObjectContainer-> |
|
9800 DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached)); |
|
9801 } |
|
9802 |
|
9803 NS_IF_ADDREF(*aState = mStateObjectCached); |
|
9804 |
|
9805 return NS_OK; |
|
9806 } |
|
9807 |
|
9808 nsDOMNavigationTiming* |
|
9809 nsDocument::GetNavigationTiming() const |
|
9810 { |
|
9811 return mTiming; |
|
9812 } |
|
9813 |
|
9814 nsresult |
|
9815 nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming) |
|
9816 { |
|
9817 mTiming = aTiming; |
|
9818 if (!mLoadingTimeStamp.IsNull() && mTiming) { |
|
9819 mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp); |
|
9820 } |
|
9821 return NS_OK; |
|
9822 } |
|
9823 |
|
9824 Element* |
|
9825 nsDocument::FindImageMap(const nsAString& aUseMapValue) |
|
9826 { |
|
9827 if (aUseMapValue.IsEmpty()) { |
|
9828 return nullptr; |
|
9829 } |
|
9830 |
|
9831 nsAString::const_iterator start, end; |
|
9832 aUseMapValue.BeginReading(start); |
|
9833 aUseMapValue.EndReading(end); |
|
9834 |
|
9835 int32_t hash = aUseMapValue.FindChar('#'); |
|
9836 if (hash < 0) { |
|
9837 return nullptr; |
|
9838 } |
|
9839 // aUsemap contains a '#', set start to point right after the '#' |
|
9840 start.advance(hash + 1); |
|
9841 |
|
9842 if (start == end) { |
|
9843 return nullptr; // aUsemap == "#" |
|
9844 } |
|
9845 |
|
9846 const nsAString& mapName = Substring(start, end); |
|
9847 |
|
9848 if (!mImageMaps) { |
|
9849 mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map); |
|
9850 } |
|
9851 |
|
9852 uint32_t i, n = mImageMaps->Length(true); |
|
9853 nsString name; |
|
9854 for (i = 0; i < n; ++i) { |
|
9855 nsIContent* map = mImageMaps->Item(i); |
|
9856 if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName, |
|
9857 eCaseMatters) || |
|
9858 (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) && |
|
9859 mapName.Equals(name, nsCaseInsensitiveStringComparator()))) { |
|
9860 return map->AsElement(); |
|
9861 } |
|
9862 } |
|
9863 |
|
9864 return nullptr; |
|
9865 } |
|
9866 |
|
9867 #define DEPRECATED_OPERATION(_op) #_op "Warning", |
|
9868 static const char* kWarnings[] = { |
|
9869 #include "nsDeprecatedOperationList.h" |
|
9870 nullptr |
|
9871 }; |
|
9872 #undef DEPRECATED_OPERATION |
|
9873 |
|
9874 void |
|
9875 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation, |
|
9876 bool asError /* = false */) |
|
9877 { |
|
9878 static_assert(eDeprecatedOperationCount <= 64, |
|
9879 "Too many deprecated operations"); |
|
9880 if (mWarnedAbout & (1ull << aOperation)) { |
|
9881 return; |
|
9882 } |
|
9883 mWarnedAbout |= (1ull << aOperation); |
|
9884 uint32_t flags = asError ? nsIScriptError::errorFlag |
|
9885 : nsIScriptError::warningFlag; |
|
9886 nsContentUtils::ReportToConsole(flags, |
|
9887 NS_LITERAL_CSTRING("DOM Core"), this, |
|
9888 nsContentUtils::eDOM_PROPERTIES, |
|
9889 kWarnings[aOperation]); |
|
9890 } |
|
9891 |
|
9892 nsresult |
|
9893 nsDocument::AddImage(imgIRequest* aImage) |
|
9894 { |
|
9895 NS_ENSURE_ARG_POINTER(aImage); |
|
9896 |
|
9897 // See if the image is already in the hashtable. If it is, get the old count. |
|
9898 uint32_t oldCount = 0; |
|
9899 mImageTracker.Get(aImage, &oldCount); |
|
9900 |
|
9901 // Put the image in the hashtable, with the proper count. |
|
9902 mImageTracker.Put(aImage, oldCount + 1); |
|
9903 |
|
9904 nsresult rv = NS_OK; |
|
9905 |
|
9906 // If this is the first insertion and we're locking images, lock this image |
|
9907 // too. |
|
9908 if (oldCount == 0) { |
|
9909 if (mLockingImages) |
|
9910 rv = aImage->LockImage(); |
|
9911 if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit || |
|
9912 mImageTracker.Count() < sOnloadDecodeLimit)) |
|
9913 rv = aImage->StartDecoding(); |
|
9914 } |
|
9915 |
|
9916 // If this is the first insertion and we're animating images, request |
|
9917 // that this image be animated too. |
|
9918 if (oldCount == 0 && mAnimatingImages) { |
|
9919 nsresult rv2 = aImage->IncrementAnimationConsumers(); |
|
9920 rv = NS_SUCCEEDED(rv) ? rv2 : rv; |
|
9921 } |
|
9922 |
|
9923 return rv; |
|
9924 } |
|
9925 |
|
9926 nsresult |
|
9927 nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags) |
|
9928 { |
|
9929 NS_ENSURE_ARG_POINTER(aImage); |
|
9930 |
|
9931 // Get the old count. It should exist and be > 0. |
|
9932 uint32_t count = 0; |
|
9933 DebugOnly<bool> found = mImageTracker.Get(aImage, &count); |
|
9934 NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!"); |
|
9935 NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!"); |
|
9936 |
|
9937 // We're removing, so decrement the count. |
|
9938 count--; |
|
9939 |
|
9940 // If the count is now zero, remove from the tracker. |
|
9941 // Otherwise, set the new value. |
|
9942 if (count != 0) { |
|
9943 mImageTracker.Put(aImage, count); |
|
9944 return NS_OK; |
|
9945 } |
|
9946 |
|
9947 mImageTracker.Remove(aImage); |
|
9948 |
|
9949 nsresult rv = NS_OK; |
|
9950 |
|
9951 // Now that we're no longer tracking this image, unlock it if we'd |
|
9952 // previously locked it. |
|
9953 if (mLockingImages) { |
|
9954 rv = aImage->UnlockImage(); |
|
9955 } |
|
9956 |
|
9957 // If we're animating images, remove our request to animate this one. |
|
9958 if (mAnimatingImages) { |
|
9959 nsresult rv2 = aImage->DecrementAnimationConsumers(); |
|
9960 rv = NS_SUCCEEDED(rv) ? rv2 : rv; |
|
9961 } |
|
9962 |
|
9963 if (aFlags & REQUEST_DISCARD) { |
|
9964 // Request that the image be discarded if nobody else holds a lock on it. |
|
9965 // Do this even if !mLockingImages, because even if we didn't just unlock |
|
9966 // this image, it might still be a candidate for discarding. |
|
9967 aImage->RequestDiscard(); |
|
9968 } |
|
9969 |
|
9970 return rv; |
|
9971 } |
|
9972 |
|
9973 nsresult |
|
9974 nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin) |
|
9975 { |
|
9976 MOZ_ASSERT(aPlugin); |
|
9977 if (!mPlugins.PutEntry(aPlugin)) { |
|
9978 return NS_ERROR_OUT_OF_MEMORY; |
|
9979 } |
|
9980 return NS_OK; |
|
9981 } |
|
9982 |
|
9983 void |
|
9984 nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin) |
|
9985 { |
|
9986 MOZ_ASSERT(aPlugin); |
|
9987 mPlugins.RemoveEntry(aPlugin); |
|
9988 } |
|
9989 |
|
9990 static bool |
|
9991 AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg) |
|
9992 { |
|
9993 nsTArray<nsIObjectLoadingContent*>* plugins = |
|
9994 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg); |
|
9995 MOZ_ASSERT(plugins); |
|
9996 aDocument->GetPlugins(*plugins); |
|
9997 return true; |
|
9998 } |
|
9999 |
|
10000 static PLDHashOperator |
|
10001 AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg) |
|
10002 { |
|
10003 nsTArray<nsIObjectLoadingContent*>* allPlugins = |
|
10004 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg); |
|
10005 MOZ_ASSERT(allPlugins); |
|
10006 allPlugins->AppendElement(aPlugin->GetKey()); |
|
10007 return PL_DHASH_NEXT; |
|
10008 } |
|
10009 |
|
10010 void |
|
10011 nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) |
|
10012 { |
|
10013 aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count()); |
|
10014 mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins); |
|
10015 EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins); |
|
10016 } |
|
10017 |
|
10018 PLDHashOperator LockEnumerator(imgIRequest* aKey, |
|
10019 uint32_t aData, |
|
10020 void* userArg) |
|
10021 { |
|
10022 aKey->LockImage(); |
|
10023 aKey->RequestDecode(); |
|
10024 return PL_DHASH_NEXT; |
|
10025 } |
|
10026 |
|
10027 PLDHashOperator UnlockEnumerator(imgIRequest* aKey, |
|
10028 uint32_t aData, |
|
10029 void* userArg) |
|
10030 { |
|
10031 aKey->UnlockImage(); |
|
10032 return PL_DHASH_NEXT; |
|
10033 } |
|
10034 |
|
10035 |
|
10036 nsresult |
|
10037 nsDocument::SetImageLockingState(bool aLocked) |
|
10038 { |
|
10039 if (XRE_GetProcessType() == GeckoProcessType_Content && |
|
10040 !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) { |
|
10041 return NS_OK; |
|
10042 } |
|
10043 |
|
10044 // If there's no change, there's nothing to do. |
|
10045 if (mLockingImages == aLocked) |
|
10046 return NS_OK; |
|
10047 |
|
10048 // Otherwise, iterate over our images and perform the appropriate action. |
|
10049 mImageTracker.EnumerateRead(aLocked ? LockEnumerator |
|
10050 : UnlockEnumerator, |
|
10051 nullptr); |
|
10052 |
|
10053 // Update state. |
|
10054 mLockingImages = aLocked; |
|
10055 |
|
10056 return NS_OK; |
|
10057 } |
|
10058 |
|
10059 PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey, |
|
10060 uint32_t aData, |
|
10061 void* userArg) |
|
10062 { |
|
10063 aKey->IncrementAnimationConsumers(); |
|
10064 return PL_DHASH_NEXT; |
|
10065 } |
|
10066 |
|
10067 PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey, |
|
10068 uint32_t aData, |
|
10069 void* userArg) |
|
10070 { |
|
10071 aKey->DecrementAnimationConsumers(); |
|
10072 return PL_DHASH_NEXT; |
|
10073 } |
|
10074 |
|
10075 void |
|
10076 nsDocument::SetImagesNeedAnimating(bool aAnimating) |
|
10077 { |
|
10078 // If there's no change, there's nothing to do. |
|
10079 if (mAnimatingImages == aAnimating) |
|
10080 return; |
|
10081 |
|
10082 // Otherwise, iterate over our images and perform the appropriate action. |
|
10083 mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator |
|
10084 : DecrementAnimationEnumerator, |
|
10085 nullptr); |
|
10086 |
|
10087 // Update state. |
|
10088 mAnimatingImages = aAnimating; |
|
10089 } |
|
10090 |
|
10091 already_AddRefed<Touch> |
|
10092 nsIDocument::CreateTouch(nsIDOMWindow* aView, |
|
10093 EventTarget* aTarget, |
|
10094 int32_t aIdentifier, |
|
10095 int32_t aPageX, int32_t aPageY, |
|
10096 int32_t aScreenX, int32_t aScreenY, |
|
10097 int32_t aClientX, int32_t aClientY, |
|
10098 int32_t aRadiusX, int32_t aRadiusY, |
|
10099 float aRotationAngle, |
|
10100 float aForce) |
|
10101 { |
|
10102 nsRefPtr<Touch> touch = new Touch(aTarget, |
|
10103 aIdentifier, |
|
10104 aPageX, aPageY, |
|
10105 aScreenX, aScreenY, |
|
10106 aClientX, aClientY, |
|
10107 aRadiusX, aRadiusY, |
|
10108 aRotationAngle, |
|
10109 aForce); |
|
10110 return touch.forget(); |
|
10111 } |
|
10112 |
|
10113 already_AddRefed<TouchList> |
|
10114 nsIDocument::CreateTouchList() |
|
10115 { |
|
10116 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
|
10117 return retval.forget(); |
|
10118 } |
|
10119 |
|
10120 already_AddRefed<TouchList> |
|
10121 nsIDocument::CreateTouchList(Touch& aTouch, |
|
10122 const Sequence<OwningNonNull<Touch> >& aTouches) |
|
10123 { |
|
10124 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
|
10125 retval->Append(&aTouch); |
|
10126 for (uint32_t i = 0; i < aTouches.Length(); ++i) { |
|
10127 retval->Append(aTouches[i].get()); |
|
10128 } |
|
10129 return retval.forget(); |
|
10130 } |
|
10131 |
|
10132 already_AddRefed<TouchList> |
|
10133 nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches) |
|
10134 { |
|
10135 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
|
10136 for (uint32_t i = 0; i < aTouches.Length(); ++i) { |
|
10137 retval->Append(aTouches[i].get()); |
|
10138 } |
|
10139 return retval.forget(); |
|
10140 } |
|
10141 |
|
10142 already_AddRefed<nsDOMCaretPosition> |
|
10143 nsIDocument::CaretPositionFromPoint(float aX, float aY) |
|
10144 { |
|
10145 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); |
|
10146 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); |
|
10147 nsPoint pt(x, y); |
|
10148 |
|
10149 FlushPendingNotifications(Flush_Layout); |
|
10150 |
|
10151 nsIPresShell *ps = GetShell(); |
|
10152 if (!ps) { |
|
10153 return nullptr; |
|
10154 } |
|
10155 |
|
10156 nsIFrame *rootFrame = ps->GetRootFrame(); |
|
10157 |
|
10158 // XUL docs, unlike HTML, have no frame tree until everything's done loading |
|
10159 if (!rootFrame) { |
|
10160 return nullptr; |
|
10161 } |
|
10162 |
|
10163 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, |
|
10164 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC); |
|
10165 if (!ptFrame) { |
|
10166 return nullptr; |
|
10167 } |
|
10168 |
|
10169 // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need |
|
10170 // to adjust to frame-relative coordinates before we can perform this call. |
|
10171 // It should also not take into account the padding of the frame. |
|
10172 nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame); |
|
10173 |
|
10174 nsFrame::ContentOffsets offsets = |
|
10175 ptFrame->GetContentOffsetsFromPoint(adjustedPoint); |
|
10176 |
|
10177 nsCOMPtr<nsIContent> node = offsets.content; |
|
10178 uint32_t offset = offsets.offset; |
|
10179 nsCOMPtr<nsIContent> anonNode = node; |
|
10180 bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree(); |
|
10181 if (nodeIsAnonymous) { |
|
10182 node = ptFrame->GetContent(); |
|
10183 nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent(); |
|
10184 nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon); |
|
10185 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon); |
|
10186 bool isText; |
|
10187 if (textArea || (input && |
|
10188 NS_SUCCEEDED(input->MozIsTextField(false, &isText)) && |
|
10189 isText)) { |
|
10190 // If the anonymous content node has a child, then we need to make sure |
|
10191 // that we get the appropriate child, as otherwise the offset may not be |
|
10192 // correct when we construct a range for it. |
|
10193 nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild(); |
|
10194 if (firstChild) { |
|
10195 anonNode = firstChild; |
|
10196 } |
|
10197 |
|
10198 if (textArea) { |
|
10199 offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset); |
|
10200 } |
|
10201 |
|
10202 node = nonanon; |
|
10203 } else { |
|
10204 node = nullptr; |
|
10205 offset = 0; |
|
10206 } |
|
10207 } |
|
10208 |
|
10209 nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset); |
|
10210 if (nodeIsAnonymous) { |
|
10211 aCaretPos->SetAnonymousContentNode(anonNode); |
|
10212 } |
|
10213 return aCaretPos.forget(); |
|
10214 } |
|
10215 |
|
10216 NS_IMETHODIMP |
|
10217 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos) |
|
10218 { |
|
10219 NS_ENSURE_ARG_POINTER(aCaretPos); |
|
10220 *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take(); |
|
10221 return NS_OK; |
|
10222 } |
|
10223 |
|
10224 void |
|
10225 nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv) |
|
10226 { |
|
10227 nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI); |
|
10228 if (NS_FAILED(res)) { |
|
10229 rv.Throw(res); |
|
10230 } |
|
10231 } |
|
10232 |
|
10233 void |
|
10234 nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv) |
|
10235 { |
|
10236 nsCOMPtr<nsIURI> uri; |
|
10237 nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI); |
|
10238 if (NS_FAILED(res)) { |
|
10239 rv.Throw(res); |
|
10240 return; |
|
10241 } |
|
10242 res = CSSLoader()->ObsoleteSheet(uri); |
|
10243 if (NS_FAILED(res)) { |
|
10244 rv.Throw(res); |
|
10245 } |
|
10246 } |
|
10247 |
|
10248 nsIHTMLCollection* |
|
10249 nsIDocument::Children() |
|
10250 { |
|
10251 if (!mChildrenCollection) { |
|
10252 mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard, |
|
10253 nsGkAtoms::_asterix, |
|
10254 nsGkAtoms::_asterix, |
|
10255 false); |
|
10256 } |
|
10257 |
|
10258 return mChildrenCollection; |
|
10259 } |
|
10260 |
|
10261 uint32_t |
|
10262 nsIDocument::ChildElementCount() |
|
10263 { |
|
10264 return Children()->Length(); |
|
10265 } |
|
10266 |
|
10267 namespace mozilla { |
|
10268 |
|
10269 // Singleton class to manage the list of fullscreen documents which are the |
|
10270 // root of a branch which contains fullscreen documents. We maintain this list |
|
10271 // so that we can easily exit all windows from fullscreen when the user |
|
10272 // presses the escape key. |
|
10273 class FullscreenRoots { |
|
10274 public: |
|
10275 // Adds a root to the manager. Adding a root multiple times does not result |
|
10276 // in duplicate entries for that item, only one. |
|
10277 static void Add(nsIDocument* aRoot); |
|
10278 |
|
10279 // Iterates over every root in the root list, and calls aFunction, passing |
|
10280 // each root once to aFunction. It is safe to call Add() and Remove() while |
|
10281 // iterating over the list (i.e. in aFunction). Documents that are removed |
|
10282 // from the manager during traversal are not traversed, and documents that |
|
10283 // are added to the manager during traversal are also not traversed. |
|
10284 static void ForEach(void(*aFunction)(nsIDocument* aDoc)); |
|
10285 |
|
10286 // Removes a specific root from the manager. |
|
10287 static void Remove(nsIDocument* aRoot); |
|
10288 |
|
10289 // Returns true if all roots added to the list have been removed. |
|
10290 static bool IsEmpty(); |
|
10291 |
|
10292 private: |
|
10293 |
|
10294 FullscreenRoots() { |
|
10295 MOZ_COUNT_CTOR(FullscreenRoots); |
|
10296 } |
|
10297 ~FullscreenRoots() { |
|
10298 MOZ_COUNT_DTOR(FullscreenRoots); |
|
10299 } |
|
10300 |
|
10301 enum { |
|
10302 NotFound = uint32_t(-1) |
|
10303 }; |
|
10304 // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound. |
|
10305 static uint32_t Find(nsIDocument* aRoot); |
|
10306 |
|
10307 // Returns true if aRoot is in the list of fullscreen roots. |
|
10308 static bool Contains(nsIDocument* aRoot); |
|
10309 |
|
10310 // Singleton instance of the FullscreenRoots. This is instantiated when a |
|
10311 // root is added, and it is deleted when the last root is removed. |
|
10312 static FullscreenRoots* sInstance; |
|
10313 |
|
10314 // List of weak pointers to roots. |
|
10315 nsTArray<nsWeakPtr> mRoots; |
|
10316 }; |
|
10317 |
|
10318 FullscreenRoots* FullscreenRoots::sInstance = nullptr; |
|
10319 |
|
10320 /* static */ |
|
10321 void |
|
10322 FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc)) |
|
10323 { |
|
10324 if (!sInstance) { |
|
10325 return; |
|
10326 } |
|
10327 // Create a copy of the roots array, and iterate over the copy. This is so |
|
10328 // that if an element is removed from mRoots we don't mess up our iteration. |
|
10329 nsTArray<nsWeakPtr> roots(sInstance->mRoots); |
|
10330 // Call aFunction on all entries. |
|
10331 for (uint32_t i = 0; i < roots.Length(); i++) { |
|
10332 nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]); |
|
10333 // Check that the root isn't in the manager. This is so that new additions |
|
10334 // while we were running don't get traversed. |
|
10335 if (root && FullscreenRoots::Contains(root)) { |
|
10336 aFunction(root); |
|
10337 } |
|
10338 } |
|
10339 } |
|
10340 |
|
10341 /* static */ |
|
10342 bool |
|
10343 FullscreenRoots::Contains(nsIDocument* aRoot) |
|
10344 { |
|
10345 return FullscreenRoots::Find(aRoot) != NotFound; |
|
10346 } |
|
10347 |
|
10348 /* static */ |
|
10349 void |
|
10350 FullscreenRoots::Add(nsIDocument* aRoot) |
|
10351 { |
|
10352 if (!FullscreenRoots::Contains(aRoot)) { |
|
10353 if (!sInstance) { |
|
10354 sInstance = new FullscreenRoots(); |
|
10355 } |
|
10356 nsWeakPtr weakRoot = do_GetWeakReference(aRoot); |
|
10357 sInstance->mRoots.AppendElement(weakRoot); |
|
10358 } |
|
10359 } |
|
10360 |
|
10361 /* static */ |
|
10362 uint32_t |
|
10363 FullscreenRoots::Find(nsIDocument* aRoot) |
|
10364 { |
|
10365 if (!sInstance) { |
|
10366 return NotFound; |
|
10367 } |
|
10368 nsTArray<nsWeakPtr>& roots = sInstance->mRoots; |
|
10369 for (uint32_t i = 0; i < roots.Length(); i++) { |
|
10370 nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i])); |
|
10371 if (otherRoot == aRoot) { |
|
10372 return i; |
|
10373 } |
|
10374 } |
|
10375 return NotFound; |
|
10376 } |
|
10377 |
|
10378 /* static */ |
|
10379 void |
|
10380 FullscreenRoots::Remove(nsIDocument* aRoot) |
|
10381 { |
|
10382 uint32_t index = Find(aRoot); |
|
10383 NS_ASSERTION(index != NotFound, |
|
10384 "Should only try to remove roots which are still added!"); |
|
10385 if (index == NotFound || !sInstance) { |
|
10386 return; |
|
10387 } |
|
10388 sInstance->mRoots.RemoveElementAt(index); |
|
10389 if (sInstance->mRoots.IsEmpty()) { |
|
10390 delete sInstance; |
|
10391 sInstance = nullptr; |
|
10392 } |
|
10393 } |
|
10394 |
|
10395 /* static */ |
|
10396 bool |
|
10397 FullscreenRoots::IsEmpty() |
|
10398 { |
|
10399 return !sInstance; |
|
10400 } |
|
10401 |
|
10402 } // end namespace mozilla. |
|
10403 using mozilla::FullscreenRoots; |
|
10404 |
|
10405 nsIDocument* |
|
10406 nsDocument::GetFullscreenRoot() |
|
10407 { |
|
10408 nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot); |
|
10409 return root; |
|
10410 } |
|
10411 |
|
10412 void |
|
10413 nsDocument::SetFullscreenRoot(nsIDocument* aRoot) |
|
10414 { |
|
10415 mFullscreenRoot = do_GetWeakReference(aRoot); |
|
10416 } |
|
10417 |
|
10418 NS_IMETHODIMP |
|
10419 nsDocument::MozCancelFullScreen() |
|
10420 { |
|
10421 nsIDocument::MozCancelFullScreen(); |
|
10422 return NS_OK; |
|
10423 } |
|
10424 |
|
10425 void |
|
10426 nsIDocument::MozCancelFullScreen() |
|
10427 { |
|
10428 RestorePreviousFullScreenState(); |
|
10429 } |
|
10430 |
|
10431 // Runnable to set window full-screen mode. Used as a script runner |
|
10432 // to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to |
|
10433 // run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event |
|
10434 // (handled in chome code) which is unsafe to run if this is called in |
|
10435 // Element::UnbindFromTree(). |
|
10436 class nsSetWindowFullScreen : public nsRunnable { |
|
10437 public: |
|
10438 nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue) |
|
10439 : mDoc(aDoc), mValue(aValue) {} |
|
10440 |
|
10441 NS_IMETHOD Run() |
|
10442 { |
|
10443 if (mDoc->GetWindow()) { |
|
10444 mDoc->GetWindow()->SetFullScreenInternal(mValue, false); |
|
10445 } |
|
10446 return NS_OK; |
|
10447 } |
|
10448 |
|
10449 private: |
|
10450 nsCOMPtr<nsIDocument> mDoc; |
|
10451 bool mValue; |
|
10452 }; |
|
10453 |
|
10454 static nsIDocument* |
|
10455 GetFullscreenRootDocument(nsIDocument* aDoc) |
|
10456 { |
|
10457 if (!aDoc) { |
|
10458 return nullptr; |
|
10459 } |
|
10460 nsIDocument* doc = aDoc; |
|
10461 nsIDocument* parent; |
|
10462 while ((parent = doc->GetParentDocument()) && |
|
10463 (!nsContentUtils::IsFullscreenApiContentOnly() || |
|
10464 !nsContentUtils::IsChromeDoc(parent))) { |
|
10465 doc = parent; |
|
10466 } |
|
10467 return doc; |
|
10468 } |
|
10469 |
|
10470 static void |
|
10471 SetWindowFullScreen(nsIDocument* aDoc, bool aValue) |
|
10472 { |
|
10473 // Maintain list of fullscreen root documents. |
|
10474 nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc); |
|
10475 if (aValue) { |
|
10476 FullscreenRoots::Add(root); |
|
10477 } else { |
|
10478 FullscreenRoots::Remove(root); |
|
10479 } |
|
10480 if (!nsContentUtils::IsFullscreenApiContentOnly()) { |
|
10481 nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue)); |
|
10482 } |
|
10483 } |
|
10484 |
|
10485 class nsCallExitFullscreen : public nsRunnable { |
|
10486 public: |
|
10487 nsCallExitFullscreen(nsIDocument* aDoc) |
|
10488 : mDoc(aDoc) {} |
|
10489 NS_IMETHOD Run() |
|
10490 { |
|
10491 nsDocument::ExitFullscreen(mDoc); |
|
10492 return NS_OK; |
|
10493 } |
|
10494 private: |
|
10495 nsCOMPtr<nsIDocument> mDoc; |
|
10496 }; |
|
10497 |
|
10498 /* static */ |
|
10499 void |
|
10500 nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync) |
|
10501 { |
|
10502 if (aDoc && !aDoc->IsFullScreenDoc()) { |
|
10503 return; |
|
10504 } |
|
10505 if (aRunAsync) { |
|
10506 NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc)); |
|
10507 return; |
|
10508 } |
|
10509 nsDocument::ExitFullscreen(aDoc); |
|
10510 } |
|
10511 |
|
10512 // Returns true if the document is a direct child of a cross process parent |
|
10513 // mozbrowser iframe. This is the case when the document has a null parent, |
|
10514 // and its DocShell reports that it is a browser frame. |
|
10515 static bool |
|
10516 HasCrossProcessParent(nsIDocument* aDocument) |
|
10517 { |
|
10518 if (XRE_GetProcessType() != GeckoProcessType_Content) { |
|
10519 return false; |
|
10520 } |
|
10521 if (aDocument->GetParentDocument() != nullptr) { |
|
10522 return false; |
|
10523 } |
|
10524 nsPIDOMWindow* win = aDocument->GetWindow(); |
|
10525 if (!win) { |
|
10526 return false; |
|
10527 } |
|
10528 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell(); |
|
10529 if (!docShell) { |
|
10530 return false; |
|
10531 } |
|
10532 return docShell->GetIsBrowserOrApp(); |
|
10533 } |
|
10534 |
|
10535 static bool |
|
10536 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) |
|
10537 { |
|
10538 if (aDoc->IsFullScreenDoc()) { |
|
10539 uint32_t* count = static_cast<uint32_t*>(aData); |
|
10540 (*count)++; |
|
10541 } |
|
10542 return true; |
|
10543 } |
|
10544 |
|
10545 static uint32_t |
|
10546 CountFullscreenSubDocuments(nsIDocument* aDoc) |
|
10547 { |
|
10548 uint32_t count = 0; |
|
10549 aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count); |
|
10550 return count; |
|
10551 } |
|
10552 |
|
10553 bool |
|
10554 nsDocument::IsFullscreenLeaf() |
|
10555 { |
|
10556 // A fullscreen leaf document is fullscreen, and has no fullscreen |
|
10557 // subdocuments. |
|
10558 if (!IsFullScreenDoc()) { |
|
10559 return false; |
|
10560 } |
|
10561 return CountFullscreenSubDocuments(this) == 0; |
|
10562 } |
|
10563 |
|
10564 static bool |
|
10565 ResetFullScreen(nsIDocument* aDocument, void* aData) |
|
10566 { |
|
10567 if (aDocument->IsFullScreenDoc()) { |
|
10568 NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1, |
|
10569 "Should have at most 1 fullscreen subdocument."); |
|
10570 static_cast<nsDocument*>(aDocument)->CleanupFullscreenState(); |
|
10571 NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen"); |
|
10572 nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData); |
|
10573 changed->AppendElement(aDocument); |
|
10574 |
|
10575 if (HasCrossProcessParent(aDocument)) { |
|
10576 // We're at the top of the content-process side doc tree. Ask the parent |
|
10577 // process to exit fullscreen. |
|
10578 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10579 os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr); |
|
10580 } |
|
10581 |
|
10582 // Dispatch a notification so that if this document has any |
|
10583 // cross-process subdocuments, they'll be notified to exit fullscreen. |
|
10584 // The BrowserElementParent listens for this event and performs the |
|
10585 // cross process notification if it has a remote child process. |
|
10586 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10587 os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr); |
|
10588 |
|
10589 aDocument->EnumerateSubDocuments(ResetFullScreen, aData); |
|
10590 } |
|
10591 return true; |
|
10592 } |
|
10593 |
|
10594 static void |
|
10595 ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) |
|
10596 { |
|
10597 MOZ_ASSERT(aMaybeNotARootDoc); |
|
10598 nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot(); |
|
10599 NS_ASSERTION(root, "Should have root when in fullscreen!"); |
|
10600 if (!root) { |
|
10601 return; |
|
10602 } |
|
10603 NS_ASSERTION(root->IsFullScreenDoc(), |
|
10604 "Fullscreen root should be a fullscreen doc..."); |
|
10605 |
|
10606 // Stores a list of documents to which we must dispatch "mozfullscreenchange". |
|
10607 // We're required by the spec to dispatch the events in leaf-to-root |
|
10608 // order when exiting fullscreen, but we traverse the doctree in a |
|
10609 // root-to-leaf order, so we save references to the documents we must |
|
10610 // dispatch to so that we dispatch in the specified order. |
|
10611 nsAutoTArray<nsIDocument*, 8> changed; |
|
10612 |
|
10613 // Walk the tree of fullscreen documents, and reset their fullscreen state. |
|
10614 ResetFullScreen(root, static_cast<void*>(&changed)); |
|
10615 |
|
10616 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse |
|
10617 // order so that the events for the leaf document arrives before the root |
|
10618 // document, as required by the spec. |
|
10619 for (uint32_t i = 0; i < changed.Length(); ++i) { |
|
10620 DispatchFullScreenChange(changed[changed.Length() - i - 1]); |
|
10621 } |
|
10622 |
|
10623 NS_ASSERTION(!root->IsFullScreenDoc(), |
|
10624 "Fullscreen root should no longer be a fullscreen doc..."); |
|
10625 |
|
10626 // Move the top-level window out of fullscreen mode. |
|
10627 SetWindowFullScreen(root, false); |
|
10628 } |
|
10629 |
|
10630 /* static */ |
|
10631 void |
|
10632 nsDocument::ExitFullscreen(nsIDocument* aDoc) |
|
10633 { |
|
10634 // Unlock the pointer, if it's locked. |
|
10635 nsCOMPtr<Element> pointerLockedElement = |
|
10636 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
10637 if (pointerLockedElement) { |
|
10638 UnlockPointer(); |
|
10639 } |
|
10640 |
|
10641 if (aDoc) { |
|
10642 ExitFullscreenInDocTree(aDoc); |
|
10643 return; |
|
10644 } |
|
10645 |
|
10646 // Clear fullscreen stacks in all fullscreen roots' descendant documents. |
|
10647 FullscreenRoots::ForEach(&ExitFullscreenInDocTree); |
|
10648 NS_ASSERTION(FullscreenRoots::IsEmpty(), |
|
10649 "Should have exited all fullscreen roots from fullscreen"); |
|
10650 } |
|
10651 |
|
10652 bool |
|
10653 GetFullscreenLeaf(nsIDocument* aDoc, void* aData) |
|
10654 { |
|
10655 if (aDoc->IsFullscreenLeaf()) { |
|
10656 nsIDocument** result = static_cast<nsIDocument**>(aData); |
|
10657 *result = aDoc; |
|
10658 return false; |
|
10659 } else if (aDoc->IsFullScreenDoc()) { |
|
10660 aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData); |
|
10661 } |
|
10662 return true; |
|
10663 } |
|
10664 |
|
10665 static nsIDocument* |
|
10666 GetFullscreenLeaf(nsIDocument* aDoc) |
|
10667 { |
|
10668 nsIDocument* leaf = nullptr; |
|
10669 GetFullscreenLeaf(aDoc, &leaf); |
|
10670 if (leaf) { |
|
10671 return leaf; |
|
10672 } |
|
10673 // Otherwise we could be either in a non-fullscreen doc tree, or we're |
|
10674 // below the fullscreen doc. Start the search from the root. |
|
10675 nsIDocument* root = GetFullscreenRootDocument(aDoc); |
|
10676 // Check that the root is actually fullscreen so we don't waste time walking |
|
10677 // around its descendants. |
|
10678 if (!root->IsFullScreenDoc()) { |
|
10679 return nullptr; |
|
10680 } |
|
10681 GetFullscreenLeaf(root, &leaf); |
|
10682 return leaf; |
|
10683 } |
|
10684 |
|
10685 void |
|
10686 nsDocument::RestorePreviousFullScreenState() |
|
10687 { |
|
10688 NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(), |
|
10689 "Should have at least 1 fullscreen root when fullscreen!"); |
|
10690 NS_ASSERTION(!nsContentUtils::IsFullscreenApiContentOnly() || |
|
10691 !nsContentUtils::IsChromeDoc(this), |
|
10692 "Should not run RestorePreviousFullScreenState() on " |
|
10693 "chrome document when fullscreen is content only"); |
|
10694 |
|
10695 if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) { |
|
10696 return; |
|
10697 } |
|
10698 |
|
10699 // If fullscreen mode is updated the pointer should be unlocked |
|
10700 nsCOMPtr<Element> pointerLockedElement = |
|
10701 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
10702 if (pointerLockedElement) { |
|
10703 UnlockPointer(); |
|
10704 } |
|
10705 |
|
10706 nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this); |
|
10707 |
|
10708 // The fullscreen document may contain a <iframe mozbrowser> element which |
|
10709 // has a cross process child. So send a notification so that its browser |
|
10710 // parent will send a message to its child process to also exit fullscreen. |
|
10711 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10712 os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr); |
|
10713 |
|
10714 // Clear full-screen stacks in all descendant in process documents, bottom up. |
|
10715 nsIDocument* doc = fullScreenDoc; |
|
10716 while (doc != this) { |
|
10717 NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc"); |
|
10718 static_cast<nsDocument*>(doc)->CleanupFullscreenState(); |
|
10719 UnlockPointer(); |
|
10720 DispatchFullScreenChange(doc); |
|
10721 doc = doc->GetParentDocument(); |
|
10722 } |
|
10723 |
|
10724 // Roll-back full-screen state to previous full-screen element. |
|
10725 NS_ASSERTION(doc == this, "Must have reached this doc."); |
|
10726 while (doc != nullptr) { |
|
10727 static_cast<nsDocument*>(doc)->FullScreenStackPop(); |
|
10728 UnlockPointer(); |
|
10729 DispatchFullScreenChange(doc); |
|
10730 if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) { |
|
10731 if (HasCrossProcessParent(doc)) { |
|
10732 // Send notification to the parent process to tell it to rollback to |
|
10733 // the previous fullscreen elements in its fullscreen element stacks. |
|
10734 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10735 os->NotifyObservers(doc, "ask-parent-to-rollback-fullscreen", nullptr); |
|
10736 } |
|
10737 // Full-screen stack in document is empty. Go back up to the parent |
|
10738 // document. We'll pop the containing element off its stack, and use |
|
10739 // its next full-screen element as the full-screen element. |
|
10740 static_cast<nsDocument*>(doc)->CleanupFullscreenState(); |
|
10741 doc = doc->GetParentDocument(); |
|
10742 } else { |
|
10743 // Else we popped the top of the stack, and there's still another |
|
10744 // element in there, so that will become the full-screen element. |
|
10745 if (fullScreenDoc != doc) { |
|
10746 // We've popped so enough off the stack that we've rolled back to |
|
10747 // a fullscreen element in a parent document. If this document isn't |
|
10748 // approved for fullscreen, or if it's cross origin, dispatch an |
|
10749 // event to chrome so it knows to show the authorization/warning UI. |
|
10750 if (!nsContentUtils::HaveEqualPrincipals(fullScreenDoc, doc) || |
|
10751 (!nsContentUtils::IsSitePermAllow(doc->NodePrincipal(), "fullscreen") && |
|
10752 !static_cast<nsDocument*>(doc)->mIsApprovedForFullscreen)) { |
|
10753 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
10754 new AsyncEventDispatcher(doc, |
|
10755 NS_LITERAL_STRING("MozEnteredDomFullscreen"), |
|
10756 true, |
|
10757 true); |
|
10758 asyncDispatcher->PostDOMEvent(); |
|
10759 } |
|
10760 } |
|
10761 |
|
10762 if (!nsContentUtils::HaveEqualPrincipals(doc, fullScreenDoc)) { |
|
10763 // The origin which is fullscreen changed. Send a notification to |
|
10764 // the root process so that a warning or approval UI can be shown |
|
10765 // as necessary. |
|
10766 nsAutoString origin; |
|
10767 nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin); |
|
10768 nsIDocument* root = GetFullscreenRootDocument(doc); |
|
10769 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10770 os->NotifyObservers(root, "fullscreen-origin-change", origin.get()); |
|
10771 } |
|
10772 |
|
10773 break; |
|
10774 } |
|
10775 } |
|
10776 |
|
10777 if (doc == nullptr) { |
|
10778 // We moved all documents in this doctree out of fullscreen mode, |
|
10779 // move the top-level window out of fullscreen mode. |
|
10780 NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(), |
|
10781 "Should have cleared all docs' stacks"); |
|
10782 SetWindowFullScreen(this, false); |
|
10783 } |
|
10784 } |
|
10785 |
|
10786 bool |
|
10787 nsDocument::IsFullScreenDoc() |
|
10788 { |
|
10789 return GetFullScreenElement() != nullptr; |
|
10790 } |
|
10791 |
|
10792 class nsCallRequestFullScreen : public nsRunnable |
|
10793 { |
|
10794 public: |
|
10795 nsCallRequestFullScreen(Element* aElement) |
|
10796 : mElement(aElement), |
|
10797 mDoc(aElement->OwnerDoc()), |
|
10798 mWasCallerChrome(nsContentUtils::IsCallerChrome()), |
|
10799 mHadRequestPending(static_cast<nsDocument*>(mDoc.get())-> |
|
10800 mAsyncFullscreenPending) |
|
10801 { |
|
10802 static_cast<nsDocument*>(mDoc.get())-> |
|
10803 mAsyncFullscreenPending = true; |
|
10804 } |
|
10805 |
|
10806 NS_IMETHOD Run() |
|
10807 { |
|
10808 static_cast<nsDocument*>(mDoc.get())-> |
|
10809 mAsyncFullscreenPending = mHadRequestPending; |
|
10810 nsDocument* doc = static_cast<nsDocument*>(mDoc.get()); |
|
10811 doc->RequestFullScreen(mElement, |
|
10812 mWasCallerChrome, |
|
10813 /* aNotifyOnOriginChange */ true); |
|
10814 return NS_OK; |
|
10815 } |
|
10816 |
|
10817 nsRefPtr<Element> mElement; |
|
10818 nsCOMPtr<nsIDocument> mDoc; |
|
10819 bool mWasCallerChrome; |
|
10820 bool mHadRequestPending; |
|
10821 }; |
|
10822 |
|
10823 void |
|
10824 nsDocument::AsyncRequestFullScreen(Element* aElement) |
|
10825 { |
|
10826 NS_ASSERTION(aElement, |
|
10827 "Must pass non-null element to nsDocument::AsyncRequestFullScreen"); |
|
10828 if (!aElement) { |
|
10829 return; |
|
10830 } |
|
10831 // Request full-screen asynchronously. |
|
10832 nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement)); |
|
10833 NS_DispatchToCurrentThread(event); |
|
10834 } |
|
10835 |
|
10836 static void |
|
10837 LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc) |
|
10838 { |
|
10839 if (!aLogFailure) { |
|
10840 return; |
|
10841 } |
|
10842 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
10843 new AsyncEventDispatcher(aDoc, |
|
10844 NS_LITERAL_STRING("mozfullscreenerror"), |
|
10845 true, |
|
10846 false); |
|
10847 asyncDispatcher->PostDOMEvent(); |
|
10848 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
10849 NS_LITERAL_CSTRING("DOM"), aDoc, |
|
10850 nsContentUtils::eDOM_PROPERTIES, |
|
10851 aMessage); |
|
10852 } |
|
10853 |
|
10854 nsresult |
|
10855 nsDocument::AddFullscreenApprovedObserver() |
|
10856 { |
|
10857 if (mHasFullscreenApprovedObserver || |
|
10858 !Preferences::GetBool("full-screen-api.approval-required")) { |
|
10859 return NS_OK; |
|
10860 } |
|
10861 |
|
10862 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10863 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE); |
|
10864 |
|
10865 nsresult res = os->AddObserver(this, "fullscreen-approved", true); |
|
10866 NS_ENSURE_SUCCESS(res, res); |
|
10867 |
|
10868 mHasFullscreenApprovedObserver = true; |
|
10869 |
|
10870 return NS_OK; |
|
10871 } |
|
10872 |
|
10873 nsresult |
|
10874 nsDocument::RemoveFullscreenApprovedObserver() |
|
10875 { |
|
10876 if (!mHasFullscreenApprovedObserver) { |
|
10877 return NS_OK; |
|
10878 } |
|
10879 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
10880 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE); |
|
10881 |
|
10882 nsresult res = os->RemoveObserver(this, "fullscreen-approved"); |
|
10883 NS_ENSURE_SUCCESS(res, res); |
|
10884 |
|
10885 mHasFullscreenApprovedObserver = false; |
|
10886 |
|
10887 return NS_OK; |
|
10888 } |
|
10889 |
|
10890 void |
|
10891 nsDocument::CleanupFullscreenState() |
|
10892 { |
|
10893 if (!mFullScreenStack.IsEmpty()) { |
|
10894 // The top element in the full-screen stack will have full-screen |
|
10895 // style bits set on it and its ancestors. Remove the style bits. |
|
10896 // Note the non-top elements won't have the style bits set. |
|
10897 Element* top = FullScreenStackTop(); |
|
10898 NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty"); |
|
10899 if (top) { |
|
10900 EventStateManager::SetFullScreenState(top, false); |
|
10901 } |
|
10902 mFullScreenStack.Clear(); |
|
10903 } |
|
10904 SetApprovedForFullscreen(false); |
|
10905 RemoveFullscreenApprovedObserver(); |
|
10906 mFullscreenRoot = nullptr; |
|
10907 } |
|
10908 |
|
10909 bool |
|
10910 nsDocument::FullScreenStackPush(Element* aElement) |
|
10911 { |
|
10912 NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()"); |
|
10913 Element* top = FullScreenStackTop(); |
|
10914 if (top == aElement || !aElement) { |
|
10915 return false; |
|
10916 } |
|
10917 if (top) { |
|
10918 // We're pushing a new element onto the full-screen stack, so we must |
|
10919 // remove the ancestor and full-screen styles from the former top of the |
|
10920 // stack. |
|
10921 EventStateManager::SetFullScreenState(top, false); |
|
10922 } |
|
10923 EventStateManager::SetFullScreenState(aElement, true); |
|
10924 nsWeakPtr weakElement = do_GetWeakReference(aElement); |
|
10925 mFullScreenStack.AppendElement(weakElement); |
|
10926 NS_ASSERTION(GetFullScreenElement() == aElement, "Should match"); |
|
10927 return true; |
|
10928 } |
|
10929 |
|
10930 void |
|
10931 nsDocument::FullScreenStackPop() |
|
10932 { |
|
10933 if (mFullScreenStack.IsEmpty()) { |
|
10934 return; |
|
10935 } |
|
10936 |
|
10937 // Remove styles from existing top element. |
|
10938 Element* top = FullScreenStackTop(); |
|
10939 EventStateManager::SetFullScreenState(top, false); |
|
10940 |
|
10941 // Remove top element. Note the remaining top element in the stack |
|
10942 // will not have full-screen style bits set, so we will need to restore |
|
10943 // them on the new top element before returning. |
|
10944 uint32_t last = mFullScreenStack.Length() - 1; |
|
10945 mFullScreenStack.RemoveElementAt(last); |
|
10946 |
|
10947 // Pop from the stack null elements (references to elements which have |
|
10948 // been GC'd since they were added to the stack) and elements which are |
|
10949 // no longer in this document. |
|
10950 while (!mFullScreenStack.IsEmpty()) { |
|
10951 Element* element = FullScreenStackTop(); |
|
10952 if (!element || !element->IsInDoc() || element->OwnerDoc() != this) { |
|
10953 NS_ASSERTION(!element->IsFullScreenAncestor(), |
|
10954 "Should have already removed full-screen styles"); |
|
10955 uint32_t last = mFullScreenStack.Length() - 1; |
|
10956 mFullScreenStack.RemoveElementAt(last); |
|
10957 } else { |
|
10958 // The top element of the stack is now an in-doc element. Apply the |
|
10959 // full-screen styles and return. |
|
10960 EventStateManager::SetFullScreenState(element, true); |
|
10961 break; |
|
10962 } |
|
10963 } |
|
10964 } |
|
10965 |
|
10966 Element* |
|
10967 nsDocument::FullScreenStackTop() |
|
10968 { |
|
10969 if (mFullScreenStack.IsEmpty()) { |
|
10970 return nullptr; |
|
10971 } |
|
10972 uint32_t last = mFullScreenStack.Length() - 1; |
|
10973 nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last])); |
|
10974 NS_ASSERTION(element, "Should have full-screen element!"); |
|
10975 NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc"); |
|
10976 NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc"); |
|
10977 return element; |
|
10978 } |
|
10979 |
|
10980 // Returns true if aDoc is in the focused tab in the active window. |
|
10981 static bool |
|
10982 IsInActiveTab(nsIDocument* aDoc) |
|
10983 { |
|
10984 nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell(); |
|
10985 if (!docshell) { |
|
10986 return false; |
|
10987 } |
|
10988 |
|
10989 bool isActive = false; |
|
10990 docshell->GetIsActive(&isActive); |
|
10991 if (!isActive) { |
|
10992 return false; |
|
10993 } |
|
10994 |
|
10995 nsCOMPtr<nsIDocShellTreeItem> rootItem; |
|
10996 docshell->GetRootTreeItem(getter_AddRefs(rootItem)); |
|
10997 if (!rootItem) { |
|
10998 return false; |
|
10999 } |
|
11000 nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem); |
|
11001 if (!rootWin) { |
|
11002 return false; |
|
11003 } |
|
11004 |
|
11005 nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
|
11006 if (!fm) { |
|
11007 return false; |
|
11008 } |
|
11009 |
|
11010 nsCOMPtr<nsIDOMWindow> activeWindow; |
|
11011 fm->GetActiveWindow(getter_AddRefs(activeWindow)); |
|
11012 if (!activeWindow) { |
|
11013 return false; |
|
11014 } |
|
11015 |
|
11016 return activeWindow == rootWin; |
|
11017 } |
|
11018 |
|
11019 nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, |
|
11020 const nsAString& aOrigin) |
|
11021 { |
|
11022 // Ensure the frame element is the fullscreen element in this document. |
|
11023 // If the frame element is already the fullscreen element in this document, |
|
11024 // this has no effect. |
|
11025 nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement)); |
|
11026 RequestFullScreen(content->AsElement(), |
|
11027 /* aWasCallerChrome */ false, |
|
11028 /* aNotifyOnOriginChange */ false); |
|
11029 |
|
11030 // Origin changed in child process, send notifiction, so that chrome can |
|
11031 // update the UI to reflect the fullscreen origin change if necessary. |
|
11032 // The BrowserElementChild listens on this, and forwards it over its |
|
11033 // parent process, where it is redispatched. Chrome (in the root process, |
|
11034 // which could be *this* process) listens for this notification so that |
|
11035 // it can show a warning or approval UI. |
|
11036 if (!aOrigin.IsEmpty()) { |
|
11037 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
11038 os->NotifyObservers(GetFullscreenRootDocument(this), |
|
11039 "fullscreen-origin-change", |
|
11040 PromiseFlatString(aOrigin).get()); |
|
11041 } |
|
11042 |
|
11043 return NS_OK; |
|
11044 } |
|
11045 |
|
11046 nsresult nsDocument::RemoteFrameFullscreenReverted() |
|
11047 { |
|
11048 RestorePreviousFullScreenState(); |
|
11049 return NS_OK; |
|
11050 } |
|
11051 |
|
11052 void |
|
11053 nsDocument::RequestFullScreen(Element* aElement, |
|
11054 bool aWasCallerChrome, |
|
11055 bool aNotifyOnOriginChange) |
|
11056 { |
|
11057 NS_ASSERTION(aElement, |
|
11058 "Must pass non-null element to nsDocument::RequestFullScreen"); |
|
11059 if (!aElement || aElement == GetFullScreenElement()) { |
|
11060 return; |
|
11061 } |
|
11062 if (!aElement->IsInDoc()) { |
|
11063 LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this); |
|
11064 return; |
|
11065 } |
|
11066 if (aElement->OwnerDoc() != this) { |
|
11067 LogFullScreenDenied(true, "FullScreenDeniedMovedDocument", this); |
|
11068 return; |
|
11069 } |
|
11070 if (!GetWindow()) { |
|
11071 LogFullScreenDenied(true, "FullScreenDeniedLostWindow", this); |
|
11072 return; |
|
11073 } |
|
11074 if (nsContentUtils::IsFullscreenApiContentOnly() && |
|
11075 nsContentUtils::IsChromeDoc(this)) { |
|
11076 // Block fullscreen requests in the chrome document when the fullscreen API |
|
11077 // is configured for content only. |
|
11078 LogFullScreenDenied(true, "FullScreenDeniedContentOnly", this); |
|
11079 return; |
|
11080 } |
|
11081 if (!IsFullScreenEnabled(aWasCallerChrome, true)) { |
|
11082 // IsFullScreenEnabled calls LogFullScreenDenied, no need to log. |
|
11083 return; |
|
11084 } |
|
11085 if (GetFullScreenElement() && |
|
11086 !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) { |
|
11087 // If this document is full-screen, only grant full-screen requests from |
|
11088 // a descendant of the current full-screen element. |
|
11089 LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this); |
|
11090 return; |
|
11091 } |
|
11092 if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) { |
|
11093 LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this); |
|
11094 return; |
|
11095 } |
|
11096 // Deny requests when a windowed plugin is focused. |
|
11097 nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
|
11098 if (!fm) { |
|
11099 NS_WARNING("Failed to retrieve focus manager in full-screen request."); |
|
11100 return; |
|
11101 } |
|
11102 nsCOMPtr<nsIDOMElement> focusedElement; |
|
11103 fm->GetFocusedElement(getter_AddRefs(focusedElement)); |
|
11104 if (focusedElement) { |
|
11105 nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement); |
|
11106 if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) { |
|
11107 LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this); |
|
11108 return; |
|
11109 } |
|
11110 } |
|
11111 |
|
11112 // Stash a reference to any existing fullscreen doc, we'll use this later |
|
11113 // to detect if the origin which is fullscreen has changed. |
|
11114 nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this); |
|
11115 |
|
11116 AddFullscreenApprovedObserver(); |
|
11117 |
|
11118 // Stores a list of documents which we must dispatch "mozfullscreenchange" |
|
11119 // too. We're required by the spec to dispatch the events in root-to-leaf |
|
11120 // order, but we traverse the doctree in a leaf-to-root order, so we save |
|
11121 // references to the documents we must dispatch to so that we get the order |
|
11122 // as specified. |
|
11123 nsAutoTArray<nsIDocument*, 8> changed; |
|
11124 |
|
11125 // Remember the root document, so that if a full-screen document is hidden |
|
11126 // we can reset full-screen state in the remaining visible full-screen documents. |
|
11127 nsIDocument* fullScreenRootDoc = GetFullscreenRootDocument(this); |
|
11128 if (fullScreenRootDoc->IsFullScreenDoc()) { |
|
11129 // A document is already in fullscreen, unlock the mouse pointer |
|
11130 // before setting a new document to fullscreen |
|
11131 UnlockPointer(); |
|
11132 } |
|
11133 |
|
11134 // If a document is already in fullscreen, then unlock the mouse pointer |
|
11135 // before setting a new document to fullscreen |
|
11136 nsCOMPtr<Element> pointerLockedElement = |
|
11137 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
11138 if (pointerLockedElement) { |
|
11139 UnlockPointer(); |
|
11140 } |
|
11141 |
|
11142 // Set the full-screen element. This sets the full-screen style on the |
|
11143 // element, and the full-screen-ancestor styles on ancestors of the element |
|
11144 // in this document. |
|
11145 DebugOnly<bool> x = FullScreenStackPush(aElement); |
|
11146 NS_ASSERTION(x, "Full-screen state of requesting doc should always change!"); |
|
11147 changed.AppendElement(this); |
|
11148 |
|
11149 // Propagate up the document hierarchy, setting the full-screen element as |
|
11150 // the element's container in ancestor documents. This also sets the |
|
11151 // appropriate css styles as well. Note we don't propagate down the |
|
11152 // document hierarchy, the full-screen element (or its container) is not |
|
11153 // visible there. Stop when we reach the root document. |
|
11154 nsIDocument* child = this; |
|
11155 while (true) { |
|
11156 child->SetFullscreenRoot(fullScreenRootDoc); |
|
11157 NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc, |
|
11158 "Fullscreen root should be set!"); |
|
11159 if (child == fullScreenRootDoc) { |
|
11160 break; |
|
11161 } |
|
11162 nsIDocument* parent = child->GetParentDocument(); |
|
11163 Element* element = parent->FindContentForSubDocument(child)->AsElement(); |
|
11164 if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) { |
|
11165 changed.AppendElement(parent); |
|
11166 child = parent; |
|
11167 } else { |
|
11168 // We've reached either the root, or a point in the doctree where the |
|
11169 // new full-screen element container is the same as the previous |
|
11170 // full-screen element's container. No more changes need to be made |
|
11171 // to the full-screen stacks of documents further up the tree. |
|
11172 break; |
|
11173 } |
|
11174 } |
|
11175 |
|
11176 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse |
|
11177 // order so that the events for the root document arrives before the leaf |
|
11178 // document, as required by the spec. |
|
11179 for (uint32_t i = 0; i < changed.Length(); ++i) { |
|
11180 DispatchFullScreenChange(changed[changed.Length() - i - 1]); |
|
11181 } |
|
11182 |
|
11183 // If this document hasn't already been approved in this session, |
|
11184 // check to see if the user has granted the fullscreen access |
|
11185 // to the document's principal's host, if it has one. Note that documents |
|
11186 // in web apps which are the same origin as the web app are considered |
|
11187 // trusted and so are automatically approved. |
|
11188 if (!mIsApprovedForFullscreen) { |
|
11189 mIsApprovedForFullscreen = |
|
11190 !Preferences::GetBool("full-screen-api.approval-required") || |
|
11191 NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED || |
|
11192 nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen"); |
|
11193 } |
|
11194 |
|
11195 // If this document, or a document with the same principal has not |
|
11196 // already been approved for fullscreen this fullscreen-session, dispatch |
|
11197 // an event so that chrome knows to pop up a warning/approval UI. |
|
11198 // Note previousFullscreenDoc=nullptr upon first entry, so we always |
|
11199 // take this path on the first time we enter fullscreen in a fullscreen |
|
11200 // session. |
|
11201 if (!mIsApprovedForFullscreen || |
|
11202 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) { |
|
11203 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
11204 new AsyncEventDispatcher(this, |
|
11205 NS_LITERAL_STRING("MozEnteredDomFullscreen"), |
|
11206 true, |
|
11207 true); |
|
11208 asyncDispatcher->PostDOMEvent(); |
|
11209 } |
|
11210 |
|
11211 #ifdef DEBUG |
|
11212 // Note assertions must run before SetWindowFullScreen() as that does |
|
11213 // synchronous event dispatch which can run script which exits full-screen! |
|
11214 NS_ASSERTION(GetFullScreenElement() == aElement, |
|
11215 "Full-screen element should be the requested element!"); |
|
11216 NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc"); |
|
11217 nsCOMPtr<nsIDOMElement> fse; |
|
11218 GetMozFullScreenElement(getter_AddRefs(fse)); |
|
11219 nsCOMPtr<nsIContent> c(do_QueryInterface(fse)); |
|
11220 NS_ASSERTION(c->AsElement() == aElement, |
|
11221 "GetMozFullScreenElement should match GetFullScreenElement()"); |
|
11222 #endif |
|
11223 |
|
11224 // The origin which is fullscreen changed, send a notifiction so that the |
|
11225 // root document knows the origin of the document which requested fullscreen. |
|
11226 // This is used for the fullscreen approval UI. If we're in a child |
|
11227 // process, the root BrowserElementChild listens for this notification, |
|
11228 // and forwards it across to its BrowserElementParent, which |
|
11229 // re-broadcasts the message for the root document in its process. |
|
11230 if (aNotifyOnOriginChange && |
|
11231 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) { |
|
11232 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
11233 nsIDocument* root = GetFullscreenRootDocument(this); |
|
11234 nsAutoString origin; |
|
11235 nsContentUtils::GetUTFOrigin(NodePrincipal(), origin); |
|
11236 os->NotifyObservers(root, "fullscreen-origin-change", origin.get()); |
|
11237 } |
|
11238 |
|
11239 // Make the window full-screen. Note we must make the state changes above |
|
11240 // before making the window full-screen, as then the document reports as |
|
11241 // being in full-screen mode when the chrome "fullscreen" event fires, |
|
11242 // enabling chrome to distinguish between browser and dom full-screen |
|
11243 // modes. Also note that nsGlobalWindow::SetFullScreen() (which |
|
11244 // SetWindowFullScreen() calls) proxies to the root window in its hierarchy, |
|
11245 // and does not operate on the a per-nsIDOMWindow basis. |
|
11246 SetWindowFullScreen(this, true); |
|
11247 } |
|
11248 |
|
11249 NS_IMETHODIMP |
|
11250 nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement) |
|
11251 { |
|
11252 ErrorResult rv; |
|
11253 Element* el = GetMozFullScreenElement(rv); |
|
11254 if (rv.Failed()) { |
|
11255 return rv.ErrorCode(); |
|
11256 } |
|
11257 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el); |
|
11258 retval.forget(aFullScreenElement); |
|
11259 return NS_OK; |
|
11260 } |
|
11261 |
|
11262 Element* |
|
11263 nsDocument::GetMozFullScreenElement(ErrorResult& rv) |
|
11264 { |
|
11265 if (IsFullScreenDoc()) { |
|
11266 // Must have a full-screen element while in full-screen mode. |
|
11267 Element* el = GetFullScreenElement(); |
|
11268 if (!el) { |
|
11269 rv.Throw(NS_ERROR_UNEXPECTED); |
|
11270 } |
|
11271 return el; |
|
11272 } |
|
11273 return nullptr; |
|
11274 } |
|
11275 |
|
11276 Element* |
|
11277 nsDocument::GetFullScreenElement() |
|
11278 { |
|
11279 Element* element = FullScreenStackTop(); |
|
11280 NS_ASSERTION(!element || |
|
11281 element->IsFullScreenAncestor(), |
|
11282 "Fullscreen element should have fullscreen styles applied"); |
|
11283 return element; |
|
11284 } |
|
11285 |
|
11286 NS_IMETHODIMP |
|
11287 nsDocument::GetMozFullScreen(bool *aFullScreen) |
|
11288 { |
|
11289 *aFullScreen = MozFullScreen(); |
|
11290 return NS_OK; |
|
11291 } |
|
11292 |
|
11293 NS_IMETHODIMP |
|
11294 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen) |
|
11295 { |
|
11296 NS_ENSURE_ARG_POINTER(aFullScreen); |
|
11297 *aFullScreen = MozFullScreenEnabled(); |
|
11298 return NS_OK; |
|
11299 } |
|
11300 |
|
11301 bool |
|
11302 nsDocument::MozFullScreenEnabled() |
|
11303 { |
|
11304 return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false); |
|
11305 } |
|
11306 |
|
11307 static bool |
|
11308 HasFullScreenSubDocument(nsIDocument* aDoc) |
|
11309 { |
|
11310 uint32_t count = CountFullscreenSubDocuments(aDoc); |
|
11311 NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!"); |
|
11312 return count >= 1; |
|
11313 } |
|
11314 |
|
11315 bool |
|
11316 nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure) |
|
11317 { |
|
11318 if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) { |
|
11319 // Chrome code can always use the full-screen API, provided it's not |
|
11320 // explicitly disabled. Note IsCallerChrome() returns true when running |
|
11321 // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an |
|
11322 // nsRunnable! |
|
11323 return true; |
|
11324 } |
|
11325 |
|
11326 if (!nsContentUtils::IsFullScreenApiEnabled()) { |
|
11327 LogFullScreenDenied(aLogFailure, "FullScreenDeniedDisabled", this); |
|
11328 return false; |
|
11329 } |
|
11330 if (!IsVisible()) { |
|
11331 LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this); |
|
11332 return false; |
|
11333 } |
|
11334 if (HasFullScreenSubDocument(this)) { |
|
11335 LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this); |
|
11336 return false; |
|
11337 } |
|
11338 |
|
11339 // Ensure that all ancestor <iframe> elements have the allowfullscreen |
|
11340 // boolean attribute set. |
|
11341 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
|
11342 bool allowed = false; |
|
11343 if (docShell) { |
|
11344 docShell->GetFullscreenAllowed(&allowed); |
|
11345 } |
|
11346 if (!allowed) { |
|
11347 LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this); |
|
11348 } |
|
11349 |
|
11350 return allowed; |
|
11351 } |
|
11352 |
|
11353 static void |
|
11354 DispatchPointerLockChange(nsIDocument* aTarget) |
|
11355 { |
|
11356 if (!aTarget) { |
|
11357 return; |
|
11358 } |
|
11359 |
|
11360 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
11361 new AsyncEventDispatcher(aTarget, |
|
11362 NS_LITERAL_STRING("mozpointerlockchange"), |
|
11363 true, |
|
11364 false); |
|
11365 asyncDispatcher->PostDOMEvent(); |
|
11366 } |
|
11367 |
|
11368 static void |
|
11369 DispatchPointerLockError(nsIDocument* aTarget) |
|
11370 { |
|
11371 if (!aTarget) { |
|
11372 return; |
|
11373 } |
|
11374 |
|
11375 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
|
11376 new AsyncEventDispatcher(aTarget, |
|
11377 NS_LITERAL_STRING("mozpointerlockerror"), |
|
11378 true, |
|
11379 false); |
|
11380 asyncDispatcher->PostDOMEvent(); |
|
11381 } |
|
11382 |
|
11383 mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest; |
|
11384 |
|
11385 class nsPointerLockPermissionRequest : public nsRunnable, |
|
11386 public nsIContentPermissionRequest |
|
11387 { |
|
11388 public: |
|
11389 nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller) |
|
11390 : mElement(do_GetWeakReference(aElement)), |
|
11391 mDocument(do_GetWeakReference(aElement->OwnerDoc())), |
|
11392 mUserInputOrChromeCaller(aUserInputOrChromeCaller) {} |
|
11393 |
|
11394 virtual ~nsPointerLockPermissionRequest() {} |
|
11395 |
|
11396 NS_DECL_ISUPPORTS |
|
11397 NS_DECL_NSICONTENTPERMISSIONREQUEST |
|
11398 |
|
11399 NS_IMETHOD Run() |
|
11400 { |
|
11401 nsCOMPtr<Element> e = do_QueryReferent(mElement); |
|
11402 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
|
11403 if (!e || !d || gPendingPointerLockRequest != this || |
|
11404 e->GetCurrentDoc() != d) { |
|
11405 Handled(); |
|
11406 DispatchPointerLockError(d); |
|
11407 return NS_OK; |
|
11408 } |
|
11409 |
|
11410 // We're about to enter fullscreen mode. |
|
11411 nsDocument* doc = static_cast<nsDocument*>(d.get()); |
|
11412 if (doc->mAsyncFullscreenPending || |
|
11413 (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) { |
|
11414 // We're still waiting for approval. |
|
11415 return NS_OK; |
|
11416 } |
|
11417 |
|
11418 if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) { |
|
11419 Allow(JS::UndefinedHandleValue); |
|
11420 return NS_OK; |
|
11421 } |
|
11422 |
|
11423 // In non-fullscreen mode user input (or chrome caller) is required! |
|
11424 // Also, don't let the page to try to get the permission too many times. |
|
11425 if (!mUserInputOrChromeCaller || |
|
11426 doc->mCancelledPointerLockRequests > 2) { |
|
11427 Handled(); |
|
11428 DispatchPointerLockError(d); |
|
11429 return NS_OK; |
|
11430 } |
|
11431 |
|
11432 // Handling a request from user input in non-fullscreen mode. |
|
11433 // Do a normal permission check. |
|
11434 nsCOMPtr<nsIContentPermissionPrompt> prompt = |
|
11435 do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); |
|
11436 if (prompt) { |
|
11437 prompt->Prompt(this); |
|
11438 } |
|
11439 |
|
11440 return NS_OK; |
|
11441 } |
|
11442 |
|
11443 void Handled() |
|
11444 { |
|
11445 mElement = nullptr; |
|
11446 mDocument = nullptr; |
|
11447 if (gPendingPointerLockRequest == this) { |
|
11448 gPendingPointerLockRequest = nullptr; |
|
11449 } |
|
11450 } |
|
11451 |
|
11452 nsWeakPtr mElement; |
|
11453 nsWeakPtr mDocument; |
|
11454 bool mUserInputOrChromeCaller; |
|
11455 }; |
|
11456 |
|
11457 NS_IMPL_ISUPPORTS_INHERITED(nsPointerLockPermissionRequest, |
|
11458 nsRunnable, |
|
11459 nsIContentPermissionRequest) |
|
11460 |
|
11461 NS_IMETHODIMP |
|
11462 nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes) |
|
11463 { |
|
11464 nsTArray<nsString> emptyOptions; |
|
11465 return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"), |
|
11466 NS_LITERAL_CSTRING("unused"), |
|
11467 emptyOptions, |
|
11468 aTypes); |
|
11469 } |
|
11470 |
|
11471 NS_IMETHODIMP |
|
11472 nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal) |
|
11473 { |
|
11474 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
|
11475 if (d) { |
|
11476 NS_ADDREF(*aPrincipal = d->NodePrincipal()); |
|
11477 } |
|
11478 return NS_OK; |
|
11479 } |
|
11480 |
|
11481 NS_IMETHODIMP |
|
11482 nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow) |
|
11483 { |
|
11484 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
|
11485 if (d) { |
|
11486 NS_IF_ADDREF(*aWindow = d->GetInnerWindow()); |
|
11487 } |
|
11488 return NS_OK; |
|
11489 } |
|
11490 |
|
11491 NS_IMETHODIMP |
|
11492 nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement) |
|
11493 { |
|
11494 // It is enough to implement GetWindow. |
|
11495 *aElement = nullptr; |
|
11496 return NS_OK; |
|
11497 } |
|
11498 |
|
11499 NS_IMETHODIMP |
|
11500 nsPointerLockPermissionRequest::Cancel() |
|
11501 { |
|
11502 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
|
11503 Handled(); |
|
11504 if (d) { |
|
11505 static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++; |
|
11506 DispatchPointerLockError(d); |
|
11507 } |
|
11508 return NS_OK; |
|
11509 } |
|
11510 |
|
11511 NS_IMETHODIMP |
|
11512 nsPointerLockPermissionRequest::Allow(JS::HandleValue aChoices) |
|
11513 { |
|
11514 MOZ_ASSERT(aChoices.isUndefined()); |
|
11515 |
|
11516 nsCOMPtr<Element> e = do_QueryReferent(mElement); |
|
11517 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
|
11518 nsDocument* d = static_cast<nsDocument*>(doc.get()); |
|
11519 if (!e || !d || gPendingPointerLockRequest != this || |
|
11520 e->GetCurrentDoc() != d || |
|
11521 (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) { |
|
11522 Handled(); |
|
11523 DispatchPointerLockError(d); |
|
11524 return NS_OK; |
|
11525 } |
|
11526 |
|
11527 // Mark handled here so that we don't need to call it everywhere below. |
|
11528 Handled(); |
|
11529 |
|
11530 nsCOMPtr<Element> pointerLockedElement = |
|
11531 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
11532 if (e == pointerLockedElement) { |
|
11533 DispatchPointerLockChange(d); |
|
11534 return NS_OK; |
|
11535 } |
|
11536 |
|
11537 // Note, we must bypass focus change, so pass true as the last parameter! |
|
11538 if (!d->ShouldLockPointer(e, pointerLockedElement, true)) { |
|
11539 DispatchPointerLockError(d); |
|
11540 return NS_OK; |
|
11541 } |
|
11542 |
|
11543 if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) { |
|
11544 DispatchPointerLockError(d); |
|
11545 return NS_OK; |
|
11546 } |
|
11547 |
|
11548 d->mCancelledPointerLockRequests = 0; |
|
11549 e->SetPointerLock(); |
|
11550 EventStateManager::sPointerLockedElement = do_GetWeakReference(e); |
|
11551 EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc); |
|
11552 NS_ASSERTION(EventStateManager::sPointerLockedElement && |
|
11553 EventStateManager::sPointerLockedDoc, |
|
11554 "aElement and this should support weak references!"); |
|
11555 |
|
11556 DispatchPointerLockChange(d); |
|
11557 return NS_OK; |
|
11558 } |
|
11559 |
|
11560 void |
|
11561 nsDocument::SetApprovedForFullscreen(bool aIsApproved) |
|
11562 { |
|
11563 mIsApprovedForFullscreen = aIsApproved; |
|
11564 } |
|
11565 |
|
11566 nsresult |
|
11567 nsDocument::Observe(nsISupports *aSubject, |
|
11568 const char *aTopic, |
|
11569 const char16_t *aData) |
|
11570 { |
|
11571 if (strcmp("fullscreen-approved", aTopic) == 0) { |
|
11572 nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject)); |
|
11573 if (subject != this) { |
|
11574 return NS_OK; |
|
11575 } |
|
11576 SetApprovedForFullscreen(true); |
|
11577 if (gPendingPointerLockRequest) { |
|
11578 // We have a request pending. Create a clone of it and re-dispatch so that |
|
11579 // Run() method gets called again. |
|
11580 nsCOMPtr<Element> el = |
|
11581 do_QueryReferent(gPendingPointerLockRequest->mElement); |
|
11582 nsCOMPtr<nsIDocument> doc = |
|
11583 do_QueryReferent(gPendingPointerLockRequest->mDocument); |
|
11584 bool userInputOrChromeCaller = |
|
11585 gPendingPointerLockRequest->mUserInputOrChromeCaller; |
|
11586 gPendingPointerLockRequest->Handled(); |
|
11587 if (doc == this && el && el->GetCurrentDoc() == doc) { |
|
11588 nsPointerLockPermissionRequest* clone = |
|
11589 new nsPointerLockPermissionRequest(el, userInputOrChromeCaller); |
|
11590 gPendingPointerLockRequest = clone; |
|
11591 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get(); |
|
11592 NS_DispatchToMainThread(r); |
|
11593 } |
|
11594 } |
|
11595 } |
|
11596 return NS_OK; |
|
11597 } |
|
11598 |
|
11599 void |
|
11600 nsDocument::RequestPointerLock(Element* aElement) |
|
11601 { |
|
11602 NS_ASSERTION(aElement, |
|
11603 "Must pass non-null element to nsDocument::RequestPointerLock"); |
|
11604 |
|
11605 nsCOMPtr<Element> pointerLockedElement = |
|
11606 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
11607 if (aElement == pointerLockedElement) { |
|
11608 DispatchPointerLockChange(this); |
|
11609 return; |
|
11610 } |
|
11611 |
|
11612 if (!ShouldLockPointer(aElement, pointerLockedElement)) { |
|
11613 DispatchPointerLockError(this); |
|
11614 return; |
|
11615 } |
|
11616 |
|
11617 bool userInputOrChromeCaller = EventStateManager::IsHandlingUserInput() || |
|
11618 nsContentUtils::IsCallerChrome(); |
|
11619 |
|
11620 gPendingPointerLockRequest = |
|
11621 new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller); |
|
11622 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get(); |
|
11623 NS_DispatchToMainThread(r); |
|
11624 } |
|
11625 |
|
11626 bool |
|
11627 nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock, |
|
11628 bool aNoFocusCheck) |
|
11629 { |
|
11630 // Check if pointer lock pref is enabled |
|
11631 if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) { |
|
11632 NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled"); |
|
11633 return false; |
|
11634 } |
|
11635 |
|
11636 if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) { |
|
11637 NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document"); |
|
11638 return false; |
|
11639 } |
|
11640 |
|
11641 if (!aElement->IsInDoc()) { |
|
11642 NS_WARNING("ShouldLockPointer(): Element without Document"); |
|
11643 return false; |
|
11644 } |
|
11645 |
|
11646 if (mSandboxFlags & SANDBOXED_POINTER_LOCK) { |
|
11647 NS_WARNING("ShouldLockPointer(): Document is sandboxed and doesn't allow pointer-lock"); |
|
11648 return false; |
|
11649 } |
|
11650 |
|
11651 // Check if the element is in a document with a docshell. |
|
11652 nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc(); |
|
11653 if (!ownerDoc->GetContainer()) { |
|
11654 return false; |
|
11655 } |
|
11656 nsCOMPtr<nsPIDOMWindow> ownerWindow = ownerDoc->GetWindow(); |
|
11657 if (!ownerWindow) { |
|
11658 return false; |
|
11659 } |
|
11660 nsCOMPtr<nsPIDOMWindow> ownerInnerWindow = ownerDoc->GetInnerWindow(); |
|
11661 if (!ownerInnerWindow) { |
|
11662 return false; |
|
11663 } |
|
11664 if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) { |
|
11665 return false; |
|
11666 } |
|
11667 |
|
11668 nsCOMPtr<nsIDOMWindow> top; |
|
11669 ownerWindow->GetScriptableTop(getter_AddRefs(top)); |
|
11670 nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top); |
|
11671 if (!piTop || !piTop->GetExtantDoc() || |
|
11672 piTop->GetExtantDoc()->Hidden()) { |
|
11673 NS_WARNING("ShouldLockPointer(): Top document isn't visible."); |
|
11674 return false; |
|
11675 } |
|
11676 |
|
11677 if (!aNoFocusCheck) { |
|
11678 mozilla::ErrorResult rv; |
|
11679 if (!piTop->GetExtantDoc()->HasFocus(rv)) { |
|
11680 NS_WARNING("ShouldLockPointer(): Top document isn't focused."); |
|
11681 return false; |
|
11682 } |
|
11683 } |
|
11684 |
|
11685 return true; |
|
11686 } |
|
11687 |
|
11688 bool |
|
11689 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle) |
|
11690 { |
|
11691 // NOTE: aElement will be nullptr when unlocking. |
|
11692 nsCOMPtr<nsPIDOMWindow> window = GetWindow(); |
|
11693 if (!window) { |
|
11694 NS_WARNING("SetPointerLock(): No Window"); |
|
11695 return false; |
|
11696 } |
|
11697 |
|
11698 nsIDocShell *docShell = window->GetDocShell(); |
|
11699 if (!docShell) { |
|
11700 NS_WARNING("SetPointerLock(): No DocShell (window already closed?)"); |
|
11701 return false; |
|
11702 } |
|
11703 |
|
11704 nsRefPtr<nsPresContext> presContext; |
|
11705 docShell->GetPresContext(getter_AddRefs(presContext)); |
|
11706 if (!presContext) { |
|
11707 NS_WARNING("SetPointerLock(): Unable to get presContext in \ |
|
11708 domWindow->GetDocShell()->GetPresContext()"); |
|
11709 return false; |
|
11710 } |
|
11711 |
|
11712 nsCOMPtr<nsIPresShell> shell = presContext->PresShell(); |
|
11713 if (!shell) { |
|
11714 NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()"); |
|
11715 return false; |
|
11716 } |
|
11717 |
|
11718 nsIFrame* rootFrame = shell->GetRootFrame(); |
|
11719 if (!rootFrame) { |
|
11720 NS_WARNING("SetPointerLock(): Unable to get root frame"); |
|
11721 return false; |
|
11722 } |
|
11723 |
|
11724 nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget(); |
|
11725 if (!widget) { |
|
11726 NS_WARNING("SetPointerLock(): Unable to find widget in \ |
|
11727 shell->GetRootFrame()->GetNearestWidget();"); |
|
11728 return false; |
|
11729 } |
|
11730 |
|
11731 if (aElement && (aElement->OwnerDoc() != this)) { |
|
11732 NS_WARNING("SetPointerLock(): Element not in this document."); |
|
11733 return false; |
|
11734 } |
|
11735 |
|
11736 // Hide the cursor and set pointer lock for future mouse events |
|
11737 nsRefPtr<EventStateManager> esm = presContext->EventStateManager(); |
|
11738 esm->SetCursor(aCursorStyle, nullptr, false, |
|
11739 0.0f, 0.0f, widget, true); |
|
11740 esm->SetPointerLock(widget, aElement); |
|
11741 |
|
11742 return true; |
|
11743 } |
|
11744 |
|
11745 void |
|
11746 nsDocument::UnlockPointer(nsIDocument* aDoc) |
|
11747 { |
|
11748 if (!EventStateManager::sIsPointerLocked) { |
|
11749 return; |
|
11750 } |
|
11751 |
|
11752 nsCOMPtr<nsIDocument> pointerLockedDoc = |
|
11753 do_QueryReferent(EventStateManager::sPointerLockedDoc); |
|
11754 if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) { |
|
11755 return; |
|
11756 } |
|
11757 nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get()); |
|
11758 if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) { |
|
11759 return; |
|
11760 } |
|
11761 |
|
11762 nsCOMPtr<Element> pointerLockedElement = |
|
11763 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
11764 if (pointerLockedElement) { |
|
11765 pointerLockedElement->ClearPointerLock(); |
|
11766 } |
|
11767 |
|
11768 EventStateManager::sPointerLockedElement = nullptr; |
|
11769 EventStateManager::sPointerLockedDoc = nullptr; |
|
11770 static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc; |
|
11771 gPendingPointerLockRequest = nullptr; |
|
11772 DispatchPointerLockChange(pointerLockedDoc); |
|
11773 } |
|
11774 |
|
11775 void |
|
11776 nsIDocument::UnlockPointer(nsIDocument* aDoc) |
|
11777 { |
|
11778 nsDocument::UnlockPointer(aDoc); |
|
11779 } |
|
11780 |
|
11781 NS_IMETHODIMP |
|
11782 nsDocument::MozExitPointerLock() |
|
11783 { |
|
11784 nsIDocument::MozExitPointerLock(); |
|
11785 return NS_OK; |
|
11786 } |
|
11787 |
|
11788 NS_IMETHODIMP |
|
11789 nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement) |
|
11790 { |
|
11791 Element* el = nsIDocument::GetMozPointerLockElement(); |
|
11792 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el); |
|
11793 retval.forget(aPointerLockedElement); |
|
11794 return NS_OK; |
|
11795 } |
|
11796 |
|
11797 Element* |
|
11798 nsIDocument::GetMozPointerLockElement() |
|
11799 { |
|
11800 nsCOMPtr<Element> pointerLockedElement = |
|
11801 do_QueryReferent(EventStateManager::sPointerLockedElement); |
|
11802 if (!pointerLockedElement) { |
|
11803 return nullptr; |
|
11804 } |
|
11805 |
|
11806 // Make sure pointer locked element is in the same document. |
|
11807 nsCOMPtr<nsIDocument> pointerLockedDoc = |
|
11808 do_QueryReferent(EventStateManager::sPointerLockedDoc); |
|
11809 if (pointerLockedDoc != this) { |
|
11810 return nullptr; |
|
11811 } |
|
11812 |
|
11813 return pointerLockedElement; |
|
11814 } |
|
11815 |
|
11816 void |
|
11817 nsDocument::XPCOMShutdown() |
|
11818 { |
|
11819 gPendingPointerLockRequest = nullptr; |
|
11820 sProcessingStack.destroyIfConstructed(); |
|
11821 } |
|
11822 |
|
11823 void |
|
11824 nsDocument::UpdateVisibilityState() |
|
11825 { |
|
11826 dom::VisibilityState oldState = mVisibilityState; |
|
11827 mVisibilityState = GetVisibilityState(); |
|
11828 if (oldState != mVisibilityState) { |
|
11829 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
|
11830 NS_LITERAL_STRING("visibilitychange"), |
|
11831 /* bubbles = */ true, |
|
11832 /* cancelable = */ false); |
|
11833 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
|
11834 NS_LITERAL_STRING("mozvisibilitychange"), |
|
11835 /* bubbles = */ true, |
|
11836 /* cancelable = */ false); |
|
11837 |
|
11838 EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
|
11839 } |
|
11840 } |
|
11841 |
|
11842 VisibilityState |
|
11843 nsDocument::GetVisibilityState() const |
|
11844 { |
|
11845 // We have to check a few pieces of information here: |
|
11846 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters. |
|
11847 // 2) Do we have an outer window? If not, we're hidden. Note that we don't |
|
11848 // want to use GetWindow here because it does weird groveling for windows |
|
11849 // in some cases. |
|
11850 // 3) Is our outer window background? If so, we're hidden. |
|
11851 // Otherwise, we're visible. |
|
11852 if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() || |
|
11853 mWindow->GetOuterWindow()->IsBackground()) { |
|
11854 return dom::VisibilityState::Hidden; |
|
11855 } |
|
11856 |
|
11857 return dom::VisibilityState::Visible; |
|
11858 } |
|
11859 |
|
11860 /* virtual */ void |
|
11861 nsDocument::PostVisibilityUpdateEvent() |
|
11862 { |
|
11863 nsCOMPtr<nsIRunnable> event = |
|
11864 NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState); |
|
11865 NS_DispatchToMainThread(event); |
|
11866 } |
|
11867 |
|
11868 NS_IMETHODIMP |
|
11869 nsDocument::GetMozHidden(bool* aHidden) |
|
11870 { |
|
11871 *aHidden = MozHidden(); |
|
11872 return NS_OK; |
|
11873 } |
|
11874 |
|
11875 NS_IMETHODIMP |
|
11876 nsDocument::GetHidden(bool* aHidden) |
|
11877 { |
|
11878 *aHidden = Hidden(); |
|
11879 return NS_OK; |
|
11880 } |
|
11881 |
|
11882 NS_IMETHODIMP |
|
11883 nsDocument::GetMozVisibilityState(nsAString& aState) |
|
11884 { |
|
11885 WarnOnceAbout(ePrefixedVisibilityAPI); |
|
11886 return GetVisibilityState(aState); |
|
11887 } |
|
11888 |
|
11889 NS_IMETHODIMP |
|
11890 nsDocument::GetVisibilityState(nsAString& aState) |
|
11891 { |
|
11892 const EnumEntry& entry = |
|
11893 VisibilityStateValues::strings[static_cast<int>(mVisibilityState)]; |
|
11894 aState.AssignASCII(entry.value, entry.length); |
|
11895 return NS_OK; |
|
11896 } |
|
11897 |
|
11898 /* virtual */ void |
|
11899 nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const |
|
11900 { |
|
11901 aWindowSizes->mDOMOtherSize += |
|
11902 nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf); |
|
11903 |
|
11904 if (mPresShell) { |
|
11905 mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf, |
|
11906 &aWindowSizes->mArenaStats, |
|
11907 &aWindowSizes->mLayoutPresShellSize, |
|
11908 &aWindowSizes->mLayoutStyleSetsSize, |
|
11909 &aWindowSizes->mLayoutTextRunsSize, |
|
11910 &aWindowSizes->mLayoutPresContextSize); |
|
11911 } |
|
11912 |
|
11913 aWindowSizes->mPropertyTablesSize += |
|
11914 mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf); |
|
11915 for (uint32_t i = 0, count = mExtraPropertyTables.Length(); |
|
11916 i < count; ++i) { |
|
11917 aWindowSizes->mPropertyTablesSize += |
|
11918 mExtraPropertyTables[i]->SizeOfExcludingThis(aWindowSizes->mMallocSizeOf); |
|
11919 } |
|
11920 |
|
11921 if (EventListenerManager* elm = GetExistingListenerManager()) { |
|
11922 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount(); |
|
11923 } |
|
11924 |
|
11925 // Measurement of the following members may be added later if DMD finds it |
|
11926 // is worthwhile: |
|
11927 // - many! |
|
11928 } |
|
11929 |
|
11930 void |
|
11931 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const |
|
11932 { |
|
11933 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this); |
|
11934 DocAddSizeOfExcludingThis(aWindowSizes); |
|
11935 } |
|
11936 |
|
11937 static size_t |
|
11938 SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet, |
|
11939 MallocSizeOf aMallocSizeOf, |
|
11940 void* aData) |
|
11941 { |
|
11942 return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf); |
|
11943 } |
|
11944 |
|
11945 size_t |
|
11946 nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
11947 { |
|
11948 // This SizeOfExcludingThis() overrides the one from nsINode. But |
|
11949 // nsDocuments can only appear at the top of the DOM tree, and we use the |
|
11950 // specialized DocAddSizeOfExcludingThis() in that case. So this should never |
|
11951 // be called. |
|
11952 MOZ_CRASH(); |
|
11953 } |
|
11954 |
|
11955 void |
|
11956 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const |
|
11957 { |
|
11958 nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes); |
|
11959 |
|
11960 for (nsIContent* node = nsINode::GetFirstChild(); |
|
11961 node; |
|
11962 node = node->GetNextNode(this)) |
|
11963 { |
|
11964 size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf); |
|
11965 size_t* p; |
|
11966 |
|
11967 switch (node->NodeType()) { |
|
11968 case nsIDOMNode::ELEMENT_NODE: |
|
11969 p = &aWindowSizes->mDOMElementNodesSize; |
|
11970 break; |
|
11971 case nsIDOMNode::TEXT_NODE: |
|
11972 p = &aWindowSizes->mDOMTextNodesSize; |
|
11973 break; |
|
11974 case nsIDOMNode::CDATA_SECTION_NODE: |
|
11975 p = &aWindowSizes->mDOMCDATANodesSize; |
|
11976 break; |
|
11977 case nsIDOMNode::COMMENT_NODE: |
|
11978 p = &aWindowSizes->mDOMCommentNodesSize; |
|
11979 break; |
|
11980 default: |
|
11981 p = &aWindowSizes->mDOMOtherSize; |
|
11982 break; |
|
11983 } |
|
11984 |
|
11985 *p += nodeSize; |
|
11986 |
|
11987 if (EventListenerManager* elm = node->GetExistingListenerManager()) { |
|
11988 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount(); |
|
11989 } |
|
11990 } |
|
11991 |
|
11992 aWindowSizes->mStyleSheetsSize += |
|
11993 mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
|
11994 aWindowSizes->mMallocSizeOf); |
|
11995 aWindowSizes->mStyleSheetsSize += |
|
11996 mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
|
11997 aWindowSizes->mMallocSizeOf); |
|
11998 aWindowSizes->mStyleSheetsSize += |
|
11999 mAdditionalSheets[eAgentSheet]. |
|
12000 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
|
12001 aWindowSizes->mMallocSizeOf); |
|
12002 aWindowSizes->mStyleSheetsSize += |
|
12003 mAdditionalSheets[eUserSheet]. |
|
12004 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
|
12005 aWindowSizes->mMallocSizeOf); |
|
12006 aWindowSizes->mStyleSheetsSize += |
|
12007 mAdditionalSheets[eAuthorSheet]. |
|
12008 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
|
12009 aWindowSizes->mMallocSizeOf); |
|
12010 // Lumping in the loader with the style-sheets size is not ideal, |
|
12011 // but most of the things in there are in fact stylesheets, so it |
|
12012 // doesn't seem worthwhile to separate it out. |
|
12013 aWindowSizes->mStyleSheetsSize += |
|
12014 CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf); |
|
12015 |
|
12016 aWindowSizes->mDOMOtherSize += |
|
12017 mAttrStyleSheet ? |
|
12018 mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) : |
|
12019 0; |
|
12020 |
|
12021 aWindowSizes->mDOMOtherSize += |
|
12022 mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf); |
|
12023 |
|
12024 aWindowSizes->mDOMOtherSize += |
|
12025 mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis, |
|
12026 aWindowSizes->mMallocSizeOf); |
|
12027 |
|
12028 // Measurement of the following members may be added later if DMD finds it |
|
12029 // is worthwhile: |
|
12030 // - many! |
|
12031 } |
|
12032 |
|
12033 NS_IMETHODIMP |
|
12034 nsDocument::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn) |
|
12035 { |
|
12036 return nsINode::QuerySelector(aSelector, aReturn); |
|
12037 } |
|
12038 |
|
12039 NS_IMETHODIMP |
|
12040 nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn) |
|
12041 { |
|
12042 return nsINode::QuerySelectorAll(aSelector, aReturn); |
|
12043 } |
|
12044 |
|
12045 already_AddRefed<nsIDocument> |
|
12046 nsIDocument::Constructor(const GlobalObject& aGlobal, |
|
12047 ErrorResult& rv) |
|
12048 { |
|
12049 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); |
|
12050 if (!global) { |
|
12051 rv.Throw(NS_ERROR_UNEXPECTED); |
|
12052 return nullptr; |
|
12053 } |
|
12054 |
|
12055 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports()); |
|
12056 if (!prin) { |
|
12057 rv.Throw(NS_ERROR_UNEXPECTED); |
|
12058 return nullptr; |
|
12059 } |
|
12060 |
|
12061 nsCOMPtr<nsIURI> uri; |
|
12062 NS_NewURI(getter_AddRefs(uri), "about:blank"); |
|
12063 if (!uri) { |
|
12064 rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
12065 return nullptr; |
|
12066 } |
|
12067 |
|
12068 nsCOMPtr<nsIDOMDocument> document; |
|
12069 nsresult res = |
|
12070 NS_NewDOMDocument(getter_AddRefs(document), |
|
12071 NullString(), |
|
12072 EmptyString(), |
|
12073 nullptr, |
|
12074 uri, |
|
12075 uri, |
|
12076 prin->GetPrincipal(), |
|
12077 true, |
|
12078 global, |
|
12079 DocumentFlavorLegacyGuess); |
|
12080 if (NS_FAILED(res)) { |
|
12081 rv.Throw(res); |
|
12082 return nullptr; |
|
12083 } |
|
12084 |
|
12085 nsCOMPtr<nsIDocument> doc = do_QueryInterface(document); |
|
12086 doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE); |
|
12087 |
|
12088 return doc.forget(); |
|
12089 } |
|
12090 |
|
12091 already_AddRefed<nsIDOMXPathExpression> |
|
12092 nsIDocument::CreateExpression(const nsAString& aExpression, |
|
12093 nsIDOMXPathNSResolver* aResolver, |
|
12094 ErrorResult& rv) |
|
12095 { |
|
12096 return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv); |
|
12097 } |
|
12098 |
|
12099 already_AddRefed<nsIDOMXPathNSResolver> |
|
12100 nsIDocument::CreateNSResolver(nsINode* aNodeResolver, |
|
12101 ErrorResult& rv) |
|
12102 { |
|
12103 return XPathEvaluator()->CreateNSResolver(aNodeResolver, rv); |
|
12104 } |
|
12105 |
|
12106 already_AddRefed<nsISupports> |
|
12107 nsIDocument::Evaluate(const nsAString& aExpression, nsINode* aContextNode, |
|
12108 nsIDOMXPathNSResolver* aResolver, uint16_t aType, |
|
12109 nsISupports* aResult, ErrorResult& rv) |
|
12110 { |
|
12111 return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType, |
|
12112 aResult, rv); |
|
12113 } |
|
12114 |
|
12115 NS_IMETHODIMP |
|
12116 nsDocument::CreateExpression(const nsAString& aExpression, |
|
12117 nsIDOMXPathNSResolver* aResolver, |
|
12118 nsIDOMXPathExpression** aResult) |
|
12119 { |
|
12120 return XPathEvaluator()->CreateExpression(aExpression, aResolver, aResult); |
|
12121 } |
|
12122 |
|
12123 NS_IMETHODIMP |
|
12124 nsDocument::CreateNSResolver(nsIDOMNode* aNodeResolver, |
|
12125 nsIDOMXPathNSResolver** aResult) |
|
12126 { |
|
12127 return XPathEvaluator()->CreateNSResolver(aNodeResolver, aResult); |
|
12128 } |
|
12129 |
|
12130 NS_IMETHODIMP |
|
12131 nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode, |
|
12132 nsIDOMXPathNSResolver* aResolver, uint16_t aType, |
|
12133 nsISupports* aInResult, nsISupports** aResult) |
|
12134 { |
|
12135 return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType, |
|
12136 aInResult, aResult); |
|
12137 } |
|
12138 |
|
12139 // This is just a hack around the fact that window.document is not |
|
12140 // [Unforgeable] yet. |
|
12141 JSObject* |
|
12142 nsIDocument::WrapObject(JSContext *aCx) |
|
12143 { |
|
12144 MOZ_ASSERT(IsDOMBinding()); |
|
12145 |
|
12146 JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx)); |
|
12147 if (!obj) { |
|
12148 return nullptr; |
|
12149 } |
|
12150 |
|
12151 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(GetInnerWindow()); |
|
12152 if (!win) { |
|
12153 // No window, nothing else to do here |
|
12154 return obj; |
|
12155 } |
|
12156 |
|
12157 if (this != win->GetExtantDoc()) { |
|
12158 // We're not the current document; we're also done here |
|
12159 return obj; |
|
12160 } |
|
12161 |
|
12162 JSAutoCompartment ac(aCx, obj); |
|
12163 |
|
12164 JS::Rooted<JS::Value> winVal(aCx); |
|
12165 nsresult rv = nsContentUtils::WrapNative(aCx, win, &NS_GET_IID(nsIDOMWindow), |
|
12166 &winVal, |
|
12167 false); |
|
12168 if (NS_FAILED(rv)) { |
|
12169 Throw(aCx, rv); |
|
12170 return nullptr; |
|
12171 } |
|
12172 |
|
12173 NS_NAMED_LITERAL_STRING(doc_str, "document"); |
|
12174 |
|
12175 if (!JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(winVal), doc_str.get(), |
|
12176 doc_str.Length(), JS::ObjectValue(*obj), |
|
12177 JS_PropertyStub, JS_StrictPropertyStub, |
|
12178 JSPROP_READONLY | JSPROP_ENUMERATE)) { |
|
12179 return nullptr; |
|
12180 } |
|
12181 |
|
12182 return obj; |
|
12183 } |
|
12184 |
|
12185 XPathEvaluator* |
|
12186 nsIDocument::XPathEvaluator() |
|
12187 { |
|
12188 if (!mXPathEvaluator) { |
|
12189 mXPathEvaluator = new dom::XPathEvaluator(this); |
|
12190 } |
|
12191 return mXPathEvaluator; |
|
12192 } |
|
12193 |
|
12194 already_AddRefed<nsIDocumentEncoder> |
|
12195 nsIDocument::GetCachedEncoder() |
|
12196 { |
|
12197 return mCachedEncoder.forget(); |
|
12198 } |
|
12199 |
|
12200 void |
|
12201 nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder) |
|
12202 { |
|
12203 mCachedEncoder = aEncoder; |
|
12204 } |
|
12205 |
|
12206 void |
|
12207 nsIDocument::SetContentTypeInternal(const nsACString& aType) |
|
12208 { |
|
12209 mCachedEncoder = nullptr; |
|
12210 mContentType = aType; |
|
12211 } |
|
12212 |
|
12213 nsILoadContext* |
|
12214 nsIDocument::GetLoadContext() const |
|
12215 { |
|
12216 return mDocumentContainer; |
|
12217 } |
|
12218 |
|
12219 nsIDocShell* |
|
12220 nsIDocument::GetDocShell() const |
|
12221 { |
|
12222 return mDocumentContainer; |
|
12223 } |
|
12224 |
|
12225 void |
|
12226 nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer) |
|
12227 { |
|
12228 mStateObjectContainer = scContainer; |
|
12229 mStateObjectCached = nullptr; |
|
12230 } |
|
12231 |
|
12232 already_AddRefed<Element> |
|
12233 nsIDocument::CreateHTMLElement(nsIAtom* aTag) |
|
12234 { |
|
12235 nsCOMPtr<nsINodeInfo> nodeInfo; |
|
12236 nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML, |
|
12237 nsIDOMNode::ELEMENT_NODE); |
|
12238 MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail"); |
|
12239 |
|
12240 nsCOMPtr<Element> element; |
|
12241 DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element), |
|
12242 nodeInfo.forget(), |
|
12243 mozilla::dom::NOT_FROM_PARSER); |
|
12244 |
|
12245 MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail"); |
|
12246 return element.forget(); |
|
12247 } |
|
12248 |
|
12249 bool |
|
12250 MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData) |
|
12251 { |
|
12252 nsCOMArray<nsIDocument>* documents = |
|
12253 static_cast<nsCOMArray<nsIDocument>*>(aData); |
|
12254 if (aDoc) { |
|
12255 aDoc->SetIsInSyncOperation(true); |
|
12256 documents->AppendObject(aDoc); |
|
12257 aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData); |
|
12258 } |
|
12259 return true; |
|
12260 } |
|
12261 |
|
12262 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc) |
|
12263 { |
|
12264 mMicroTaskLevel = nsContentUtils::MicroTaskLevel(); |
|
12265 nsContentUtils::SetMicroTaskLevel(0); |
|
12266 if (aDoc) { |
|
12267 nsPIDOMWindow* win = aDoc->GetWindow(); |
|
12268 if (win) { |
|
12269 nsCOMPtr<nsIDOMWindow> topWindow; |
|
12270 win->GetTop(getter_AddRefs(topWindow)); |
|
12271 nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow); |
|
12272 if (top) { |
|
12273 nsCOMPtr<nsIDocument> doc = top->GetExtantDoc(); |
|
12274 MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments); |
|
12275 } |
|
12276 } |
|
12277 } |
|
12278 } |
|
12279 |
|
12280 nsAutoSyncOperation::~nsAutoSyncOperation() |
|
12281 { |
|
12282 for (int32_t i = 0; i < mDocuments.Count(); ++i) { |
|
12283 mDocuments[i]->SetIsInSyncOperation(false); |
|
12284 } |
|
12285 nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel); |
|
12286 } |
|
12287 |