Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 sw=2 et tw=78: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * Base class for all our document implementations. |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "nsDocument.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "mozilla/ArrayUtils.h" |
michael@0 | 14 | #include "mozilla/AutoRestore.h" |
michael@0 | 15 | #include "mozilla/DebugOnly.h" |
michael@0 | 16 | #include "mozilla/MemoryReporting.h" |
michael@0 | 17 | #include "mozilla/Likely.h" |
michael@0 | 18 | #include <algorithm> |
michael@0 | 19 | |
michael@0 | 20 | #ifdef MOZ_LOGGING |
michael@0 | 21 | // so we can get logging even in release builds |
michael@0 | 22 | #define FORCE_PR_LOG 1 |
michael@0 | 23 | #endif |
michael@0 | 24 | #include "prlog.h" |
michael@0 | 25 | #include "plstr.h" |
michael@0 | 26 | #include "prprf.h" |
michael@0 | 27 | |
michael@0 | 28 | #include "mozilla/Telemetry.h" |
michael@0 | 29 | #include "nsIInterfaceRequestor.h" |
michael@0 | 30 | #include "nsIInterfaceRequestorUtils.h" |
michael@0 | 31 | #include "nsUnicharUtils.h" |
michael@0 | 32 | #include "nsContentList.h" |
michael@0 | 33 | #include "nsIObserver.h" |
michael@0 | 34 | #include "nsIBaseWindow.h" |
michael@0 | 35 | #include "mozilla/css/Loader.h" |
michael@0 | 36 | #include "mozilla/css/ImageLoader.h" |
michael@0 | 37 | #include "nsDocShell.h" |
michael@0 | 38 | #include "nsIDocShellTreeItem.h" |
michael@0 | 39 | #include "nsCOMArray.h" |
michael@0 | 40 | #include "nsDOMClassInfo.h" |
michael@0 | 41 | #include "nsCxPusher.h" |
michael@0 | 42 | |
michael@0 | 43 | #include "mozilla/AsyncEventDispatcher.h" |
michael@0 | 44 | #include "mozilla/BasicEvents.h" |
michael@0 | 45 | #include "mozilla/EventListenerManager.h" |
michael@0 | 46 | #include "mozilla/EventStateManager.h" |
michael@0 | 47 | #include "nsIDOMNodeFilter.h" |
michael@0 | 48 | |
michael@0 | 49 | #include "nsIDOMStyleSheet.h" |
michael@0 | 50 | #include "mozilla/dom/Attr.h" |
michael@0 | 51 | #include "nsIDOMDOMImplementation.h" |
michael@0 | 52 | #include "nsIDOMDocumentXBL.h" |
michael@0 | 53 | #include "mozilla/dom/Element.h" |
michael@0 | 54 | #include "nsGenericHTMLElement.h" |
michael@0 | 55 | #include "mozilla/dom/CDATASection.h" |
michael@0 | 56 | #include "mozilla/dom/ProcessingInstruction.h" |
michael@0 | 57 | #include "nsDOMString.h" |
michael@0 | 58 | #include "nsNodeUtils.h" |
michael@0 | 59 | #include "nsLayoutUtils.h" // for GetFrameForPoint |
michael@0 | 60 | #include "nsIFrame.h" |
michael@0 | 61 | #include "nsITabChild.h" |
michael@0 | 62 | |
michael@0 | 63 | #include "nsRange.h" |
michael@0 | 64 | #include "nsIDOMText.h" |
michael@0 | 65 | #include "nsIDOMComment.h" |
michael@0 | 66 | #include "mozilla/dom/DocumentType.h" |
michael@0 | 67 | #include "mozilla/dom/NodeIterator.h" |
michael@0 | 68 | #include "mozilla/dom/TreeWalker.h" |
michael@0 | 69 | |
michael@0 | 70 | #include "nsIServiceManager.h" |
michael@0 | 71 | |
michael@0 | 72 | #include "nsContentCID.h" |
michael@0 | 73 | #include "nsError.h" |
michael@0 | 74 | #include "nsPresShell.h" |
michael@0 | 75 | #include "nsPresContext.h" |
michael@0 | 76 | #include "nsIJSON.h" |
michael@0 | 77 | #include "nsThreadUtils.h" |
michael@0 | 78 | #include "nsNodeInfoManager.h" |
michael@0 | 79 | #include "nsIFileChannel.h" |
michael@0 | 80 | #include "nsIMultiPartChannel.h" |
michael@0 | 81 | #include "nsIRefreshURI.h" |
michael@0 | 82 | #include "nsIWebNavigation.h" |
michael@0 | 83 | #include "nsIScriptError.h" |
michael@0 | 84 | #include "nsStyleSheetService.h" |
michael@0 | 85 | |
michael@0 | 86 | #include "nsNetUtil.h" // for NS_MakeAbsoluteURI |
michael@0 | 87 | |
michael@0 | 88 | #include "nsIScriptSecurityManager.h" |
michael@0 | 89 | #include "nsIPrincipal.h" |
michael@0 | 90 | |
michael@0 | 91 | #include "nsIDOMWindow.h" |
michael@0 | 92 | #include "nsPIDOMWindow.h" |
michael@0 | 93 | #include "nsIDOMElement.h" |
michael@0 | 94 | #include "nsFocusManager.h" |
michael@0 | 95 | |
michael@0 | 96 | // for radio group stuff |
michael@0 | 97 | #include "nsIDOMHTMLInputElement.h" |
michael@0 | 98 | #include "nsIRadioVisitor.h" |
michael@0 | 99 | #include "nsIFormControl.h" |
michael@0 | 100 | |
michael@0 | 101 | #include "nsBidiUtils.h" |
michael@0 | 102 | |
michael@0 | 103 | #include "nsIDOMUserDataHandler.h" |
michael@0 | 104 | #include "nsIDOMXPathExpression.h" |
michael@0 | 105 | #include "nsIDOMXPathNSResolver.h" |
michael@0 | 106 | #include "nsIParserService.h" |
michael@0 | 107 | #include "nsContentCreatorFunctions.h" |
michael@0 | 108 | |
michael@0 | 109 | #include "nsIScriptContext.h" |
michael@0 | 110 | #include "nsBindingManager.h" |
michael@0 | 111 | #include "nsIDOMHTMLDocument.h" |
michael@0 | 112 | #include "nsHTMLDocument.h" |
michael@0 | 113 | #include "nsIDOMHTMLFormElement.h" |
michael@0 | 114 | #include "nsIRequest.h" |
michael@0 | 115 | #include "nsHostObjectProtocolHandler.h" |
michael@0 | 116 | |
michael@0 | 117 | #include "nsCharsetAlias.h" |
michael@0 | 118 | #include "nsCharsetSource.h" |
michael@0 | 119 | #include "nsIParser.h" |
michael@0 | 120 | #include "nsIContentSink.h" |
michael@0 | 121 | |
michael@0 | 122 | #include "nsDateTimeFormatCID.h" |
michael@0 | 123 | #include "nsIDateTimeFormat.h" |
michael@0 | 124 | #include "mozilla/EventDispatcher.h" |
michael@0 | 125 | #include "mozilla/EventStates.h" |
michael@0 | 126 | #include "mozilla/InternalMutationEvent.h" |
michael@0 | 127 | #include "nsDOMCID.h" |
michael@0 | 128 | |
michael@0 | 129 | #include "jsapi.h" |
michael@0 | 130 | #include "nsIXPConnect.h" |
michael@0 | 131 | #include "nsCCUncollectableMarker.h" |
michael@0 | 132 | #include "nsIContentPolicy.h" |
michael@0 | 133 | #include "nsContentPolicyUtils.h" |
michael@0 | 134 | #include "nsICategoryManager.h" |
michael@0 | 135 | #include "nsIDocumentLoaderFactory.h" |
michael@0 | 136 | #include "nsIDocumentLoader.h" |
michael@0 | 137 | #include "nsIContentViewer.h" |
michael@0 | 138 | #include "nsIXMLContentSink.h" |
michael@0 | 139 | #include "nsIXULDocument.h" |
michael@0 | 140 | #include "nsIPrompt.h" |
michael@0 | 141 | #include "nsIPropertyBag2.h" |
michael@0 | 142 | #include "nsIDOMPageTransitionEvent.h" |
michael@0 | 143 | #include "nsIDOMStyleRuleChangeEvent.h" |
michael@0 | 144 | #include "nsIDOMStyleSheetChangeEvent.h" |
michael@0 | 145 | #include "nsIDOMStyleSheetApplicableStateChangeEvent.h" |
michael@0 | 146 | #include "nsJSUtils.h" |
michael@0 | 147 | #include "nsFrameLoader.h" |
michael@0 | 148 | #include "nsEscape.h" |
michael@0 | 149 | #include "nsObjectLoadingContent.h" |
michael@0 | 150 | #include "nsHtml5TreeOpExecutor.h" |
michael@0 | 151 | #include "nsIDOMElementReplaceEvent.h" |
michael@0 | 152 | #include "mozilla/dom/HTMLLinkElement.h" |
michael@0 | 153 | #include "mozilla/dom/HTMLMediaElement.h" |
michael@0 | 154 | #ifdef MOZ_MEDIA_NAVIGATOR |
michael@0 | 155 | #include "mozilla/MediaManager.h" |
michael@0 | 156 | #endif // MOZ_MEDIA_NAVIGATOR |
michael@0 | 157 | #ifdef MOZ_WEBRTC |
michael@0 | 158 | #include "IPeerConnection.h" |
michael@0 | 159 | #endif // MOZ_WEBRTC |
michael@0 | 160 | |
michael@0 | 161 | #include "mozAutoDocUpdate.h" |
michael@0 | 162 | #include "nsGlobalWindow.h" |
michael@0 | 163 | #include "mozilla/dom/EncodingUtils.h" |
michael@0 | 164 | #include "mozilla/dom/quota/QuotaManager.h" |
michael@0 | 165 | #include "nsDOMNavigationTiming.h" |
michael@0 | 166 | |
michael@0 | 167 | #include "nsSMILAnimationController.h" |
michael@0 | 168 | #include "imgIContainer.h" |
michael@0 | 169 | #include "nsSVGUtils.h" |
michael@0 | 170 | #include "SVGElementFactory.h" |
michael@0 | 171 | |
michael@0 | 172 | #include "nsRefreshDriver.h" |
michael@0 | 173 | |
michael@0 | 174 | // FOR CSP (autogenerated by xpidl) |
michael@0 | 175 | #include "nsIContentSecurityPolicy.h" |
michael@0 | 176 | #include "nsCSPService.h" |
michael@0 | 177 | #include "nsHTMLStyleSheet.h" |
michael@0 | 178 | #include "nsHTMLCSSStyleSheet.h" |
michael@0 | 179 | #include "mozilla/dom/DOMImplementation.h" |
michael@0 | 180 | #include "mozilla/dom/ShadowRoot.h" |
michael@0 | 181 | #include "mozilla/dom/Comment.h" |
michael@0 | 182 | #include "nsTextNode.h" |
michael@0 | 183 | #include "mozilla/dom/Link.h" |
michael@0 | 184 | #include "mozilla/dom/HTMLElementBinding.h" |
michael@0 | 185 | #include "mozilla/dom/SVGElementBinding.h" |
michael@0 | 186 | #include "nsXULAppAPI.h" |
michael@0 | 187 | #include "mozilla/dom/Touch.h" |
michael@0 | 188 | #include "mozilla/dom/TouchEvent.h" |
michael@0 | 189 | #include "GeneratedEvents.h" |
michael@0 | 190 | |
michael@0 | 191 | #include "mozilla/Preferences.h" |
michael@0 | 192 | |
michael@0 | 193 | #include "imgILoader.h" |
michael@0 | 194 | #include "imgRequestProxy.h" |
michael@0 | 195 | #include "nsWrapperCacheInlines.h" |
michael@0 | 196 | #include "nsSandboxFlags.h" |
michael@0 | 197 | #include "nsIAppsService.h" |
michael@0 | 198 | #include "mozilla/dom/BindingUtils.h" |
michael@0 | 199 | #include "mozilla/dom/DocumentFragment.h" |
michael@0 | 200 | #include "mozilla/dom/Event.h" |
michael@0 | 201 | #include "mozilla/dom/HTMLBodyElement.h" |
michael@0 | 202 | #include "mozilla/dom/HTMLInputElement.h" |
michael@0 | 203 | #include "mozilla/dom/NodeFilterBinding.h" |
michael@0 | 204 | #include "mozilla/dom/OwningNonNull.h" |
michael@0 | 205 | #include "mozilla/dom/UndoManager.h" |
michael@0 | 206 | #include "mozilla/dom/WebComponentsBinding.h" |
michael@0 | 207 | #include "nsFrame.h" |
michael@0 | 208 | #include "nsDOMCaretPosition.h" |
michael@0 | 209 | #include "nsIDOMHTMLTextAreaElement.h" |
michael@0 | 210 | #include "nsViewportInfo.h" |
michael@0 | 211 | #include "nsIContentPermissionPrompt.h" |
michael@0 | 212 | #include "mozilla/StaticPtr.h" |
michael@0 | 213 | #include "nsITextControlElement.h" |
michael@0 | 214 | #include "nsIDOMNSEditableElement.h" |
michael@0 | 215 | #include "nsIEditor.h" |
michael@0 | 216 | #include "nsIDOMCSSStyleRule.h" |
michael@0 | 217 | #include "mozilla/css/Rule.h" |
michael@0 | 218 | #include "nsIDOMLocation.h" |
michael@0 | 219 | #include "nsIHttpChannelInternal.h" |
michael@0 | 220 | #include "nsISecurityConsoleMessage.h" |
michael@0 | 221 | #include "nsCharSeparatedTokenizer.h" |
michael@0 | 222 | #include "mozilla/dom/XPathEvaluator.h" |
michael@0 | 223 | #include "nsIDocumentEncoder.h" |
michael@0 | 224 | #include "nsIStructuredCloneContainer.h" |
michael@0 | 225 | #include "nsIMutableArray.h" |
michael@0 | 226 | #include "nsContentPermissionHelper.h" |
michael@0 | 227 | #include "mozilla/dom/DOMStringList.h" |
michael@0 | 228 | #include "nsWindowMemoryReporter.h" |
michael@0 | 229 | |
michael@0 | 230 | using namespace mozilla; |
michael@0 | 231 | using namespace mozilla::dom; |
michael@0 | 232 | |
michael@0 | 233 | typedef nsTArray<Link*> LinkArray; |
michael@0 | 234 | |
michael@0 | 235 | #ifdef PR_LOGGING |
michael@0 | 236 | static PRLogModuleInfo* gDocumentLeakPRLog; |
michael@0 | 237 | static PRLogModuleInfo* gCspPRLog; |
michael@0 | 238 | #endif |
michael@0 | 239 | |
michael@0 | 240 | #define NAME_NOT_VALID ((nsSimpleContentList*)1) |
michael@0 | 241 | |
michael@0 | 242 | nsIdentifierMapEntry::~nsIdentifierMapEntry() |
michael@0 | 243 | { |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | void |
michael@0 | 247 | nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback) |
michael@0 | 248 | { |
michael@0 | 249 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
michael@0 | 250 | "mIdentifierMap mNameContentList"); |
michael@0 | 251 | aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList)); |
michael@0 | 252 | |
michael@0 | 253 | if (mImageElement) { |
michael@0 | 254 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, |
michael@0 | 255 | "mIdentifierMap mImageElement element"); |
michael@0 | 256 | nsIContent* imageElement = mImageElement; |
michael@0 | 257 | aCallback->NoteXPCOMChild(imageElement); |
michael@0 | 258 | } |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | bool |
michael@0 | 262 | nsIdentifierMapEntry::IsEmpty() |
michael@0 | 263 | { |
michael@0 | 264 | return mIdContentList.Count() == 0 && !mNameContentList && |
michael@0 | 265 | !mChangeCallbacks && !mImageElement; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | Element* |
michael@0 | 269 | nsIdentifierMapEntry::GetIdElement() |
michael@0 | 270 | { |
michael@0 | 271 | return static_cast<Element*>(mIdContentList.SafeElementAt(0)); |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | Element* |
michael@0 | 275 | nsIdentifierMapEntry::GetImageIdElement() |
michael@0 | 276 | { |
michael@0 | 277 | return mImageElement ? mImageElement.get() : GetIdElement(); |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | void |
michael@0 | 281 | nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements) |
michael@0 | 282 | { |
michael@0 | 283 | for (int32_t i = 0; i < mIdContentList.Count(); ++i) { |
michael@0 | 284 | aElements->AppendObject(static_cast<Element*>(mIdContentList[i])); |
michael@0 | 285 | } |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | void |
michael@0 | 289 | nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, |
michael@0 | 290 | void* aData, bool aForImage) |
michael@0 | 291 | { |
michael@0 | 292 | if (!mChangeCallbacks) { |
michael@0 | 293 | mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>; |
michael@0 | 294 | if (!mChangeCallbacks) |
michael@0 | 295 | return; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | ChangeCallback cc = { aCallback, aData, aForImage }; |
michael@0 | 299 | mChangeCallbacks->PutEntry(cc); |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | void |
michael@0 | 303 | nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, |
michael@0 | 304 | void* aData, bool aForImage) |
michael@0 | 305 | { |
michael@0 | 306 | if (!mChangeCallbacks) |
michael@0 | 307 | return; |
michael@0 | 308 | ChangeCallback cc = { aCallback, aData, aForImage }; |
michael@0 | 309 | mChangeCallbacks->RemoveEntry(cc); |
michael@0 | 310 | if (mChangeCallbacks->Count() == 0) { |
michael@0 | 311 | mChangeCallbacks = nullptr; |
michael@0 | 312 | } |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | struct FireChangeArgs { |
michael@0 | 316 | Element* mFrom; |
michael@0 | 317 | Element* mTo; |
michael@0 | 318 | bool mImageOnly; |
michael@0 | 319 | bool mHaveImageOverride; |
michael@0 | 320 | }; |
michael@0 | 321 | |
michael@0 | 322 | namespace mozilla { |
michael@0 | 323 | namespace dom { |
michael@0 | 324 | |
michael@0 | 325 | static PLDHashOperator |
michael@0 | 326 | CustomDefinitionsTraverse(CustomElementHashKey* aKey, |
michael@0 | 327 | CustomElementDefinition* aDefinition, |
michael@0 | 328 | void* aArg) |
michael@0 | 329 | { |
michael@0 | 330 | nsCycleCollectionTraversalCallback* cb = |
michael@0 | 331 | static_cast<nsCycleCollectionTraversalCallback*>(aArg); |
michael@0 | 332 | |
michael@0 | 333 | nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks; |
michael@0 | 334 | |
michael@0 | 335 | if (callbacks->mAttributeChangedCallback.WasPassed()) { |
michael@0 | 336 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 337 | "mCustomDefinitions->mCallbacks->mAttributeChangedCallback"); |
michael@0 | 338 | cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value()); |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | if (callbacks->mCreatedCallback.WasPassed()) { |
michael@0 | 342 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 343 | "mCustomDefinitions->mCallbacks->mCreatedCallback"); |
michael@0 | 344 | cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value()); |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | if (callbacks->mAttachedCallback.WasPassed()) { |
michael@0 | 348 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 349 | "mCustomDefinitions->mCallbacks->mAttachedCallback"); |
michael@0 | 350 | cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value()); |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | if (callbacks->mDetachedCallback.WasPassed()) { |
michael@0 | 354 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 355 | "mCustomDefinitions->mCallbacks->mDetachedCallback"); |
michael@0 | 356 | cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value()); |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | return PL_DHASH_NEXT; |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | static PLDHashOperator |
michael@0 | 363 | CandidatesTraverse(CustomElementHashKey* aKey, |
michael@0 | 364 | nsTArray<nsRefPtr<Element>>* aData, |
michael@0 | 365 | void* aArg) |
michael@0 | 366 | { |
michael@0 | 367 | nsCycleCollectionTraversalCallback *cb = |
michael@0 | 368 | static_cast<nsCycleCollectionTraversalCallback*>(aArg); |
michael@0 | 369 | for (size_t i = 0; i < aData->Length(); ++i) { |
michael@0 | 370 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element"); |
michael@0 | 371 | cb->NoteXPCOMChild(aData->ElementAt(i)); |
michael@0 | 372 | } |
michael@0 | 373 | return PL_DHASH_NEXT; |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | struct CustomDefinitionTraceArgs |
michael@0 | 377 | { |
michael@0 | 378 | const TraceCallbacks& callbacks; |
michael@0 | 379 | void* closure; |
michael@0 | 380 | }; |
michael@0 | 381 | |
michael@0 | 382 | static PLDHashOperator |
michael@0 | 383 | CustomDefinitionTrace(CustomElementHashKey *aKey, |
michael@0 | 384 | CustomElementDefinition *aData, |
michael@0 | 385 | void *aArg) |
michael@0 | 386 | { |
michael@0 | 387 | CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg); |
michael@0 | 388 | MOZ_ASSERT(aData, "Definition must not be null"); |
michael@0 | 389 | traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype", |
michael@0 | 390 | traceArgs->closure); |
michael@0 | 391 | return PL_DHASH_NEXT; |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | NS_IMPL_CYCLE_COLLECTION_CLASS(Registry) |
michael@0 | 395 | |
michael@0 | 396 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry) |
michael@0 | 397 | CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure }; |
michael@0 | 398 | tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace, |
michael@0 | 399 | &customDefinitionArgs); |
michael@0 | 400 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
michael@0 | 401 | |
michael@0 | 402 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry) |
michael@0 | 403 | tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb); |
michael@0 | 404 | tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb); |
michael@0 | 405 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
michael@0 | 406 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 407 | |
michael@0 | 408 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry) |
michael@0 | 409 | tmp->mCustomDefinitions.Clear(); |
michael@0 | 410 | tmp->mCandidatesMap.Clear(); |
michael@0 | 411 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 412 | |
michael@0 | 413 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry) |
michael@0 | 414 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
michael@0 | 415 | NS_INTERFACE_MAP_END |
michael@0 | 416 | |
michael@0 | 417 | NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry) |
michael@0 | 418 | NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry) |
michael@0 | 419 | |
michael@0 | 420 | Registry::Registry() |
michael@0 | 421 | { |
michael@0 | 422 | mozilla::HoldJSObjects(this); |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | Registry::~Registry() |
michael@0 | 426 | { |
michael@0 | 427 | mozilla::DropJSObjects(this); |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | void |
michael@0 | 431 | CustomElementCallback::Call() |
michael@0 | 432 | { |
michael@0 | 433 | ErrorResult rv; |
michael@0 | 434 | switch (mType) { |
michael@0 | 435 | case nsIDocument::eCreated: |
michael@0 | 436 | // For the duration of this callback invocation, the element is being created |
michael@0 | 437 | // flag must be set to true. |
michael@0 | 438 | mOwnerData->mElementIsBeingCreated = true; |
michael@0 | 439 | mOwnerData->mCreatedCallbackInvoked = true; |
michael@0 | 440 | static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv); |
michael@0 | 441 | mOwnerData->mElementIsBeingCreated = false; |
michael@0 | 442 | break; |
michael@0 | 443 | case nsIDocument::eAttached: |
michael@0 | 444 | static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv); |
michael@0 | 445 | break; |
michael@0 | 446 | case nsIDocument::eDetached: |
michael@0 | 447 | static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv); |
michael@0 | 448 | break; |
michael@0 | 449 | case nsIDocument::eAttributeChanged: |
michael@0 | 450 | static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject, |
michael@0 | 451 | mArgs.name, mArgs.oldValue, mArgs.newValue, rv); |
michael@0 | 452 | break; |
michael@0 | 453 | } |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | void |
michael@0 | 457 | CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const |
michael@0 | 458 | { |
michael@0 | 459 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject"); |
michael@0 | 460 | aCb.NoteXPCOMChild(mThisObject); |
michael@0 | 461 | |
michael@0 | 462 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback"); |
michael@0 | 463 | aCb.NoteXPCOMChild(mCallback); |
michael@0 | 464 | } |
michael@0 | 465 | |
michael@0 | 466 | CustomElementCallback::CustomElementCallback(Element* aThisObject, |
michael@0 | 467 | nsIDocument::ElementCallbackType aCallbackType, |
michael@0 | 468 | mozilla::dom::CallbackFunction* aCallback, |
michael@0 | 469 | CustomElementData* aOwnerData) |
michael@0 | 470 | : mThisObject(aThisObject), |
michael@0 | 471 | mCallback(aCallback), |
michael@0 | 472 | mType(aCallbackType), |
michael@0 | 473 | mOwnerData(aOwnerData) |
michael@0 | 474 | { |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype, |
michael@0 | 478 | nsIAtom* aType, |
michael@0 | 479 | nsIAtom* aLocalName, |
michael@0 | 480 | LifecycleCallbacks* aCallbacks, |
michael@0 | 481 | uint32_t aNamespaceID, |
michael@0 | 482 | uint32_t aDocOrder) |
michael@0 | 483 | : mPrototype(aPrototype), |
michael@0 | 484 | mType(aType), |
michael@0 | 485 | mLocalName(aLocalName), |
michael@0 | 486 | mCallbacks(aCallbacks), |
michael@0 | 487 | mNamespaceID(aNamespaceID), |
michael@0 | 488 | mDocOrder(aDocOrder) |
michael@0 | 489 | { |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | CustomElementData::CustomElementData(nsIAtom* aType) |
michael@0 | 493 | : mType(aType), |
michael@0 | 494 | mCurrentCallback(-1), |
michael@0 | 495 | mElementIsBeingCreated(false), |
michael@0 | 496 | mCreatedCallbackInvoked(true), |
michael@0 | 497 | mAssociatedMicroTask(-1) |
michael@0 | 498 | { |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | void |
michael@0 | 502 | CustomElementData::RunCallbackQueue() |
michael@0 | 503 | { |
michael@0 | 504 | // Note: It's possible to re-enter this method. |
michael@0 | 505 | while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) { |
michael@0 | 506 | mCallbackQueue[mCurrentCallback]->Call(); |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | mCallbackQueue.Clear(); |
michael@0 | 510 | mCurrentCallback = -1; |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | } // namespace dom |
michael@0 | 514 | } // namespace mozilla |
michael@0 | 515 | |
michael@0 | 516 | static PLDHashOperator |
michael@0 | 517 | FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg) |
michael@0 | 518 | { |
michael@0 | 519 | FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg); |
michael@0 | 520 | // Don't fire image changes for non-image observers, and don't fire element |
michael@0 | 521 | // changes for image observers when an image override is active. |
michael@0 | 522 | if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) : |
michael@0 | 523 | args->mImageOnly) |
michael@0 | 524 | return PL_DHASH_NEXT; |
michael@0 | 525 | return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData) |
michael@0 | 526 | ? PL_DHASH_NEXT : PL_DHASH_REMOVE; |
michael@0 | 527 | } |
michael@0 | 528 | |
michael@0 | 529 | void |
michael@0 | 530 | nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement, |
michael@0 | 531 | Element* aNewElement, |
michael@0 | 532 | bool aImageOnly) |
michael@0 | 533 | { |
michael@0 | 534 | if (!mChangeCallbacks) |
michael@0 | 535 | return; |
michael@0 | 536 | |
michael@0 | 537 | FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement }; |
michael@0 | 538 | mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args); |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | bool |
michael@0 | 542 | nsIdentifierMapEntry::AddIdElement(Element* aElement) |
michael@0 | 543 | { |
michael@0 | 544 | NS_PRECONDITION(aElement, "Must have element"); |
michael@0 | 545 | NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0, |
michael@0 | 546 | "Why is null in our list?"); |
michael@0 | 547 | |
michael@0 | 548 | #ifdef DEBUG |
michael@0 | 549 | Element* currentElement = |
michael@0 | 550 | static_cast<Element*>(mIdContentList.SafeElementAt(0)); |
michael@0 | 551 | #endif |
michael@0 | 552 | |
michael@0 | 553 | // Common case |
michael@0 | 554 | if (mIdContentList.Count() == 0) { |
michael@0 | 555 | if (!mIdContentList.AppendElement(aElement)) |
michael@0 | 556 | return false; |
michael@0 | 557 | NS_ASSERTION(currentElement == nullptr, "How did that happen?"); |
michael@0 | 558 | FireChangeCallbacks(nullptr, aElement); |
michael@0 | 559 | return true; |
michael@0 | 560 | } |
michael@0 | 561 | |
michael@0 | 562 | // We seem to have multiple content nodes for the same id, or XUL is messing |
michael@0 | 563 | // with us. Search for the right place to insert the content. |
michael@0 | 564 | int32_t start = 0; |
michael@0 | 565 | int32_t end = mIdContentList.Count(); |
michael@0 | 566 | do { |
michael@0 | 567 | NS_ASSERTION(start < end, "Bogus start/end"); |
michael@0 | 568 | |
michael@0 | 569 | int32_t cur = (start + end) / 2; |
michael@0 | 570 | NS_ASSERTION(cur >= start && cur < end, "What happened here?"); |
michael@0 | 571 | |
michael@0 | 572 | Element* curElement = static_cast<Element*>(mIdContentList[cur]); |
michael@0 | 573 | if (curElement == aElement) { |
michael@0 | 574 | // Already in the list, so already in the right spot. Get out of here. |
michael@0 | 575 | // XXXbz this only happens because XUL does all sorts of random |
michael@0 | 576 | // UpdateIdTableEntry calls. Hate, hate, hate! |
michael@0 | 577 | return true; |
michael@0 | 578 | } |
michael@0 | 579 | |
michael@0 | 580 | if (nsContentUtils::PositionIsBefore(aElement, curElement)) { |
michael@0 | 581 | end = cur; |
michael@0 | 582 | } else { |
michael@0 | 583 | start = cur + 1; |
michael@0 | 584 | } |
michael@0 | 585 | } while (start != end); |
michael@0 | 586 | |
michael@0 | 587 | if (!mIdContentList.InsertElementAt(aElement, start)) |
michael@0 | 588 | return false; |
michael@0 | 589 | |
michael@0 | 590 | if (start == 0) { |
michael@0 | 591 | Element* oldElement = |
michael@0 | 592 | static_cast<Element*>(mIdContentList.SafeElementAt(1)); |
michael@0 | 593 | NS_ASSERTION(currentElement == oldElement, "How did that happen?"); |
michael@0 | 594 | FireChangeCallbacks(oldElement, aElement); |
michael@0 | 595 | } |
michael@0 | 596 | return true; |
michael@0 | 597 | } |
michael@0 | 598 | |
michael@0 | 599 | void |
michael@0 | 600 | nsIdentifierMapEntry::RemoveIdElement(Element* aElement) |
michael@0 | 601 | { |
michael@0 | 602 | NS_PRECONDITION(aElement, "Missing element"); |
michael@0 | 603 | |
michael@0 | 604 | // This should only be called while the document is in an update. |
michael@0 | 605 | // Assertions near the call to this method guarantee this. |
michael@0 | 606 | |
michael@0 | 607 | // This could fire in OOM situations |
michael@0 | 608 | // Only assert this in HTML documents for now as XUL does all sorts of weird |
michael@0 | 609 | // crap. |
michael@0 | 610 | NS_ASSERTION(!aElement->OwnerDoc()->IsHTML() || |
michael@0 | 611 | mIdContentList.IndexOf(aElement) >= 0, |
michael@0 | 612 | "Removing id entry that doesn't exist"); |
michael@0 | 613 | |
michael@0 | 614 | // XXXbz should this ever Compact() I guess when all the content is gone |
michael@0 | 615 | // we'll just get cleaned up in the natural order of things... |
michael@0 | 616 | Element* currentElement = |
michael@0 | 617 | static_cast<Element*>(mIdContentList.SafeElementAt(0)); |
michael@0 | 618 | mIdContentList.RemoveElement(aElement); |
michael@0 | 619 | if (currentElement == aElement) { |
michael@0 | 620 | FireChangeCallbacks(currentElement, |
michael@0 | 621 | static_cast<Element*>(mIdContentList.SafeElementAt(0))); |
michael@0 | 622 | } |
michael@0 | 623 | } |
michael@0 | 624 | |
michael@0 | 625 | void |
michael@0 | 626 | nsIdentifierMapEntry::SetImageElement(Element* aElement) |
michael@0 | 627 | { |
michael@0 | 628 | Element* oldElement = GetImageIdElement(); |
michael@0 | 629 | mImageElement = aElement; |
michael@0 | 630 | Element* newElement = GetImageIdElement(); |
michael@0 | 631 | if (oldElement != newElement) { |
michael@0 | 632 | FireChangeCallbacks(oldElement, newElement, true); |
michael@0 | 633 | } |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | void |
michael@0 | 637 | nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) |
michael@0 | 638 | { |
michael@0 | 639 | if (!mNameContentList) { |
michael@0 | 640 | mNameContentList = new nsSimpleContentList(aNode); |
michael@0 | 641 | } |
michael@0 | 642 | |
michael@0 | 643 | mNameContentList->AppendElement(aElement); |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | void |
michael@0 | 647 | nsIdentifierMapEntry::RemoveNameElement(Element* aElement) |
michael@0 | 648 | { |
michael@0 | 649 | if (mNameContentList) { |
michael@0 | 650 | mNameContentList->RemoveElement(aElement); |
michael@0 | 651 | } |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | bool |
michael@0 | 655 | nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() |
michael@0 | 656 | { |
michael@0 | 657 | Element* idElement = GetIdElement(); |
michael@0 | 658 | return idElement && |
michael@0 | 659 | nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement); |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | // static |
michael@0 | 663 | size_t |
michael@0 | 664 | nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry, |
michael@0 | 665 | MallocSizeOf aMallocSizeOf, |
michael@0 | 666 | void*) |
michael@0 | 667 | { |
michael@0 | 668 | return aEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | // Helper structs for the content->subdoc map |
michael@0 | 672 | |
michael@0 | 673 | class SubDocMapEntry : public PLDHashEntryHdr |
michael@0 | 674 | { |
michael@0 | 675 | public: |
michael@0 | 676 | // Both of these are strong references |
michael@0 | 677 | Element *mKey; // must be first, to look like PLDHashEntryStub |
michael@0 | 678 | nsIDocument *mSubDocument; |
michael@0 | 679 | }; |
michael@0 | 680 | |
michael@0 | 681 | struct FindContentData |
michael@0 | 682 | { |
michael@0 | 683 | FindContentData(nsIDocument *aSubDoc) |
michael@0 | 684 | : mSubDocument(aSubDoc), mResult(nullptr) |
michael@0 | 685 | { |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | nsISupports *mSubDocument; |
michael@0 | 689 | Element *mResult; |
michael@0 | 690 | }; |
michael@0 | 691 | |
michael@0 | 692 | |
michael@0 | 693 | /** |
michael@0 | 694 | * A struct that holds all the information about a radio group. |
michael@0 | 695 | */ |
michael@0 | 696 | struct nsRadioGroupStruct |
michael@0 | 697 | { |
michael@0 | 698 | nsRadioGroupStruct() |
michael@0 | 699 | : mRequiredRadioCount(0) |
michael@0 | 700 | , mGroupSuffersFromValueMissing(false) |
michael@0 | 701 | {} |
michael@0 | 702 | |
michael@0 | 703 | /** |
michael@0 | 704 | * A strong pointer to the currently selected radio button. |
michael@0 | 705 | */ |
michael@0 | 706 | nsRefPtr<HTMLInputElement> mSelectedRadioButton; |
michael@0 | 707 | nsCOMArray<nsIFormControl> mRadioButtons; |
michael@0 | 708 | uint32_t mRequiredRadioCount; |
michael@0 | 709 | bool mGroupSuffersFromValueMissing; |
michael@0 | 710 | }; |
michael@0 | 711 | |
michael@0 | 712 | |
michael@0 | 713 | nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument) |
michael@0 | 714 | { |
michael@0 | 715 | mLength = -1; |
michael@0 | 716 | // Not reference counted to avoid circular references. |
michael@0 | 717 | // The document will tell us when its going away. |
michael@0 | 718 | mDocument = aDocument; |
michael@0 | 719 | mDocument->AddObserver(this); |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | nsDOMStyleSheetList::~nsDOMStyleSheetList() |
michael@0 | 723 | { |
michael@0 | 724 | if (mDocument) { |
michael@0 | 725 | mDocument->RemoveObserver(this); |
michael@0 | 726 | } |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList, |
michael@0 | 730 | nsIDocumentObserver, |
michael@0 | 731 | nsIMutationObserver) |
michael@0 | 732 | |
michael@0 | 733 | uint32_t |
michael@0 | 734 | nsDOMStyleSheetList::Length() |
michael@0 | 735 | { |
michael@0 | 736 | if (!mDocument) { |
michael@0 | 737 | return 0; |
michael@0 | 738 | } |
michael@0 | 739 | |
michael@0 | 740 | // XXX Find the number and then cache it. We'll use the |
michael@0 | 741 | // observer notification to figure out if new ones have |
michael@0 | 742 | // been added or removed. |
michael@0 | 743 | if (-1 == mLength) { |
michael@0 | 744 | mLength = mDocument->GetNumberOfStyleSheets(); |
michael@0 | 745 | |
michael@0 | 746 | #ifdef DEBUG |
michael@0 | 747 | int32_t i; |
michael@0 | 748 | for (i = 0; i < mLength; i++) { |
michael@0 | 749 | nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i); |
michael@0 | 750 | nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet)); |
michael@0 | 751 | NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet"); |
michael@0 | 752 | } |
michael@0 | 753 | #endif |
michael@0 | 754 | } |
michael@0 | 755 | return mLength; |
michael@0 | 756 | } |
michael@0 | 757 | |
michael@0 | 758 | nsCSSStyleSheet* |
michael@0 | 759 | nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound) |
michael@0 | 760 | { |
michael@0 | 761 | if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) { |
michael@0 | 762 | aFound = false; |
michael@0 | 763 | return nullptr; |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | aFound = true; |
michael@0 | 767 | nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex); |
michael@0 | 768 | NS_ASSERTION(sheet, "Must have a sheet"); |
michael@0 | 769 | |
michael@0 | 770 | return static_cast<nsCSSStyleSheet*>(sheet); |
michael@0 | 771 | } |
michael@0 | 772 | |
michael@0 | 773 | void |
michael@0 | 774 | nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode) |
michael@0 | 775 | { |
michael@0 | 776 | mDocument = nullptr; |
michael@0 | 777 | } |
michael@0 | 778 | |
michael@0 | 779 | void |
michael@0 | 780 | nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument, |
michael@0 | 781 | nsIStyleSheet* aStyleSheet, |
michael@0 | 782 | bool aDocumentSheet) |
michael@0 | 783 | { |
michael@0 | 784 | if (aDocumentSheet && -1 != mLength) { |
michael@0 | 785 | nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet)); |
michael@0 | 786 | if (domss) { |
michael@0 | 787 | mLength++; |
michael@0 | 788 | } |
michael@0 | 789 | } |
michael@0 | 790 | } |
michael@0 | 791 | |
michael@0 | 792 | void |
michael@0 | 793 | nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument, |
michael@0 | 794 | nsIStyleSheet* aStyleSheet, |
michael@0 | 795 | bool aDocumentSheet) |
michael@0 | 796 | { |
michael@0 | 797 | if (aDocumentSheet && -1 != mLength) { |
michael@0 | 798 | nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet)); |
michael@0 | 799 | if (domss) { |
michael@0 | 800 | mLength--; |
michael@0 | 801 | } |
michael@0 | 802 | } |
michael@0 | 803 | } |
michael@0 | 804 | |
michael@0 | 805 | // nsOnloadBlocker implementation |
michael@0 | 806 | NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest) |
michael@0 | 807 | |
michael@0 | 808 | NS_IMETHODIMP |
michael@0 | 809 | nsOnloadBlocker::GetName(nsACString &aResult) |
michael@0 | 810 | { |
michael@0 | 811 | aResult.AssignLiteral("about:document-onload-blocker"); |
michael@0 | 812 | return NS_OK; |
michael@0 | 813 | } |
michael@0 | 814 | |
michael@0 | 815 | NS_IMETHODIMP |
michael@0 | 816 | nsOnloadBlocker::IsPending(bool *_retval) |
michael@0 | 817 | { |
michael@0 | 818 | *_retval = true; |
michael@0 | 819 | return NS_OK; |
michael@0 | 820 | } |
michael@0 | 821 | |
michael@0 | 822 | NS_IMETHODIMP |
michael@0 | 823 | nsOnloadBlocker::GetStatus(nsresult *status) |
michael@0 | 824 | { |
michael@0 | 825 | *status = NS_OK; |
michael@0 | 826 | return NS_OK; |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | NS_IMETHODIMP |
michael@0 | 830 | nsOnloadBlocker::Cancel(nsresult status) |
michael@0 | 831 | { |
michael@0 | 832 | return NS_OK; |
michael@0 | 833 | } |
michael@0 | 834 | NS_IMETHODIMP |
michael@0 | 835 | nsOnloadBlocker::Suspend(void) |
michael@0 | 836 | { |
michael@0 | 837 | return NS_OK; |
michael@0 | 838 | } |
michael@0 | 839 | NS_IMETHODIMP |
michael@0 | 840 | nsOnloadBlocker::Resume(void) |
michael@0 | 841 | { |
michael@0 | 842 | return NS_OK; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | NS_IMETHODIMP |
michael@0 | 846 | nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup) |
michael@0 | 847 | { |
michael@0 | 848 | *aLoadGroup = nullptr; |
michael@0 | 849 | return NS_OK; |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | NS_IMETHODIMP |
michael@0 | 853 | nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup) |
michael@0 | 854 | { |
michael@0 | 855 | return NS_OK; |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | NS_IMETHODIMP |
michael@0 | 859 | nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags) |
michael@0 | 860 | { |
michael@0 | 861 | *aLoadFlags = nsIRequest::LOAD_NORMAL; |
michael@0 | 862 | return NS_OK; |
michael@0 | 863 | } |
michael@0 | 864 | |
michael@0 | 865 | NS_IMETHODIMP |
michael@0 | 866 | nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) |
michael@0 | 867 | { |
michael@0 | 868 | return NS_OK; |
michael@0 | 869 | } |
michael@0 | 870 | |
michael@0 | 871 | // ================================================================== |
michael@0 | 872 | |
michael@0 | 873 | nsExternalResourceMap::nsExternalResourceMap() |
michael@0 | 874 | : mHaveShutDown(false) |
michael@0 | 875 | { |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | nsIDocument* |
michael@0 | 879 | nsExternalResourceMap::RequestResource(nsIURI* aURI, |
michael@0 | 880 | nsINode* aRequestingNode, |
michael@0 | 881 | nsDocument* aDisplayDocument, |
michael@0 | 882 | ExternalResourceLoad** aPendingLoad) |
michael@0 | 883 | { |
michael@0 | 884 | // If we ever start allowing non-same-origin loads here, we might need to do |
michael@0 | 885 | // something interesting with aRequestingPrincipal even for the hashtable |
michael@0 | 886 | // gets. |
michael@0 | 887 | NS_PRECONDITION(aURI, "Must have a URI"); |
michael@0 | 888 | NS_PRECONDITION(aRequestingNode, "Must have a node"); |
michael@0 | 889 | *aPendingLoad = nullptr; |
michael@0 | 890 | if (mHaveShutDown) { |
michael@0 | 891 | return nullptr; |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | // First, make sure we strip the ref from aURI. |
michael@0 | 895 | nsCOMPtr<nsIURI> clone; |
michael@0 | 896 | nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone)); |
michael@0 | 897 | if (NS_FAILED(rv) || !clone) { |
michael@0 | 898 | return nullptr; |
michael@0 | 899 | } |
michael@0 | 900 | |
michael@0 | 901 | ExternalResource* resource; |
michael@0 | 902 | mMap.Get(clone, &resource); |
michael@0 | 903 | if (resource) { |
michael@0 | 904 | return resource->mDocument; |
michael@0 | 905 | } |
michael@0 | 906 | |
michael@0 | 907 | nsRefPtr<PendingLoad> load; |
michael@0 | 908 | mPendingLoads.Get(clone, getter_AddRefs(load)); |
michael@0 | 909 | if (load) { |
michael@0 | 910 | load.forget(aPendingLoad); |
michael@0 | 911 | return nullptr; |
michael@0 | 912 | } |
michael@0 | 913 | |
michael@0 | 914 | load = new PendingLoad(aDisplayDocument); |
michael@0 | 915 | |
michael@0 | 916 | mPendingLoads.Put(clone, load); |
michael@0 | 917 | |
michael@0 | 918 | if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) { |
michael@0 | 919 | // Make sure we don't thrash things by trying this load again, since |
michael@0 | 920 | // chances are it failed for good reasons (security check, etc). |
michael@0 | 921 | AddExternalResource(clone, nullptr, nullptr, aDisplayDocument); |
michael@0 | 922 | } else { |
michael@0 | 923 | load.forget(aPendingLoad); |
michael@0 | 924 | } |
michael@0 | 925 | |
michael@0 | 926 | return nullptr; |
michael@0 | 927 | } |
michael@0 | 928 | |
michael@0 | 929 | struct |
michael@0 | 930 | nsExternalResourceEnumArgs |
michael@0 | 931 | { |
michael@0 | 932 | nsIDocument::nsSubDocEnumFunc callback; |
michael@0 | 933 | void *data; |
michael@0 | 934 | }; |
michael@0 | 935 | |
michael@0 | 936 | static PLDHashOperator |
michael@0 | 937 | ExternalResourceEnumerator(nsIURI* aKey, |
michael@0 | 938 | nsExternalResourceMap::ExternalResource* aData, |
michael@0 | 939 | void* aClosure) |
michael@0 | 940 | { |
michael@0 | 941 | nsExternalResourceEnumArgs* args = |
michael@0 | 942 | static_cast<nsExternalResourceEnumArgs*>(aClosure); |
michael@0 | 943 | bool next = |
michael@0 | 944 | aData->mDocument ? args->callback(aData->mDocument, args->data) : true; |
michael@0 | 945 | return next ? PL_DHASH_NEXT : PL_DHASH_STOP; |
michael@0 | 946 | } |
michael@0 | 947 | |
michael@0 | 948 | void |
michael@0 | 949 | nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, |
michael@0 | 950 | void* aData) |
michael@0 | 951 | { |
michael@0 | 952 | nsExternalResourceEnumArgs args = { aCallback, aData }; |
michael@0 | 953 | mMap.EnumerateRead(ExternalResourceEnumerator, &args); |
michael@0 | 954 | } |
michael@0 | 955 | |
michael@0 | 956 | static PLDHashOperator |
michael@0 | 957 | ExternalResourceTraverser(nsIURI* aKey, |
michael@0 | 958 | nsExternalResourceMap::ExternalResource* aData, |
michael@0 | 959 | void* aClosure) |
michael@0 | 960 | { |
michael@0 | 961 | nsCycleCollectionTraversalCallback *cb = |
michael@0 | 962 | static_cast<nsCycleCollectionTraversalCallback*>(aClosure); |
michael@0 | 963 | |
michael@0 | 964 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 965 | "mExternalResourceMap.mMap entry" |
michael@0 | 966 | "->mDocument"); |
michael@0 | 967 | cb->NoteXPCOMChild(aData->mDocument); |
michael@0 | 968 | |
michael@0 | 969 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 970 | "mExternalResourceMap.mMap entry" |
michael@0 | 971 | "->mViewer"); |
michael@0 | 972 | cb->NoteXPCOMChild(aData->mViewer); |
michael@0 | 973 | |
michael@0 | 974 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 975 | "mExternalResourceMap.mMap entry" |
michael@0 | 976 | "->mLoadGroup"); |
michael@0 | 977 | cb->NoteXPCOMChild(aData->mLoadGroup); |
michael@0 | 978 | |
michael@0 | 979 | return PL_DHASH_NEXT; |
michael@0 | 980 | } |
michael@0 | 981 | |
michael@0 | 982 | void |
michael@0 | 983 | nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const |
michael@0 | 984 | { |
michael@0 | 985 | // mPendingLoads will get cleared out as the requests complete, so |
michael@0 | 986 | // no need to worry about those here. |
michael@0 | 987 | mMap.EnumerateRead(ExternalResourceTraverser, aCallback); |
michael@0 | 988 | } |
michael@0 | 989 | |
michael@0 | 990 | static PLDHashOperator |
michael@0 | 991 | ExternalResourceHider(nsIURI* aKey, |
michael@0 | 992 | nsExternalResourceMap::ExternalResource* aData, |
michael@0 | 993 | void* aClosure) |
michael@0 | 994 | { |
michael@0 | 995 | if (aData->mViewer) { |
michael@0 | 996 | aData->mViewer->Hide(); |
michael@0 | 997 | } |
michael@0 | 998 | return PL_DHASH_NEXT; |
michael@0 | 999 | } |
michael@0 | 1000 | |
michael@0 | 1001 | void |
michael@0 | 1002 | nsExternalResourceMap::HideViewers() |
michael@0 | 1003 | { |
michael@0 | 1004 | mMap.EnumerateRead(ExternalResourceHider, nullptr); |
michael@0 | 1005 | } |
michael@0 | 1006 | |
michael@0 | 1007 | static PLDHashOperator |
michael@0 | 1008 | ExternalResourceShower(nsIURI* aKey, |
michael@0 | 1009 | nsExternalResourceMap::ExternalResource* aData, |
michael@0 | 1010 | void* aClosure) |
michael@0 | 1011 | { |
michael@0 | 1012 | if (aData->mViewer) { |
michael@0 | 1013 | aData->mViewer->Show(); |
michael@0 | 1014 | } |
michael@0 | 1015 | return PL_DHASH_NEXT; |
michael@0 | 1016 | } |
michael@0 | 1017 | |
michael@0 | 1018 | void |
michael@0 | 1019 | nsExternalResourceMap::ShowViewers() |
michael@0 | 1020 | { |
michael@0 | 1021 | mMap.EnumerateRead(ExternalResourceShower, nullptr); |
michael@0 | 1022 | } |
michael@0 | 1023 | |
michael@0 | 1024 | void |
michael@0 | 1025 | TransferZoomLevels(nsIDocument* aFromDoc, |
michael@0 | 1026 | nsIDocument* aToDoc) |
michael@0 | 1027 | { |
michael@0 | 1028 | NS_ABORT_IF_FALSE(aFromDoc && aToDoc, |
michael@0 | 1029 | "transferring zoom levels from/to null doc"); |
michael@0 | 1030 | |
michael@0 | 1031 | nsIPresShell* fromShell = aFromDoc->GetShell(); |
michael@0 | 1032 | if (!fromShell) |
michael@0 | 1033 | return; |
michael@0 | 1034 | |
michael@0 | 1035 | nsPresContext* fromCtxt = fromShell->GetPresContext(); |
michael@0 | 1036 | if (!fromCtxt) |
michael@0 | 1037 | return; |
michael@0 | 1038 | |
michael@0 | 1039 | nsIPresShell* toShell = aToDoc->GetShell(); |
michael@0 | 1040 | if (!toShell) |
michael@0 | 1041 | return; |
michael@0 | 1042 | |
michael@0 | 1043 | nsPresContext* toCtxt = toShell->GetPresContext(); |
michael@0 | 1044 | if (!toCtxt) |
michael@0 | 1045 | return; |
michael@0 | 1046 | |
michael@0 | 1047 | toCtxt->SetFullZoom(fromCtxt->GetFullZoom()); |
michael@0 | 1048 | toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize()); |
michael@0 | 1049 | toCtxt->SetTextZoom(fromCtxt->TextZoom()); |
michael@0 | 1050 | } |
michael@0 | 1051 | |
michael@0 | 1052 | void |
michael@0 | 1053 | TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc) |
michael@0 | 1054 | { |
michael@0 | 1055 | NS_ABORT_IF_FALSE(aFromDoc && aToDoc, |
michael@0 | 1056 | "transferring showing state from/to null doc"); |
michael@0 | 1057 | |
michael@0 | 1058 | if (aFromDoc->IsShowing()) { |
michael@0 | 1059 | aToDoc->OnPageShow(true, nullptr); |
michael@0 | 1060 | } |
michael@0 | 1061 | } |
michael@0 | 1062 | |
michael@0 | 1063 | nsresult |
michael@0 | 1064 | nsExternalResourceMap::AddExternalResource(nsIURI* aURI, |
michael@0 | 1065 | nsIContentViewer* aViewer, |
michael@0 | 1066 | nsILoadGroup* aLoadGroup, |
michael@0 | 1067 | nsIDocument* aDisplayDocument) |
michael@0 | 1068 | { |
michael@0 | 1069 | NS_PRECONDITION(aURI, "Unexpected call"); |
michael@0 | 1070 | NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup), |
michael@0 | 1071 | "Must have both or neither"); |
michael@0 | 1072 | |
michael@0 | 1073 | nsRefPtr<PendingLoad> load; |
michael@0 | 1074 | mPendingLoads.Get(aURI, getter_AddRefs(load)); |
michael@0 | 1075 | mPendingLoads.Remove(aURI); |
michael@0 | 1076 | |
michael@0 | 1077 | nsresult rv = NS_OK; |
michael@0 | 1078 | |
michael@0 | 1079 | nsCOMPtr<nsIDocument> doc; |
michael@0 | 1080 | if (aViewer) { |
michael@0 | 1081 | doc = aViewer->GetDocument(); |
michael@0 | 1082 | NS_ASSERTION(doc, "Must have a document"); |
michael@0 | 1083 | |
michael@0 | 1084 | nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc); |
michael@0 | 1085 | if (xulDoc) { |
michael@0 | 1086 | // We don't handle XUL stuff here yet. |
michael@0 | 1087 | rv = NS_ERROR_NOT_AVAILABLE; |
michael@0 | 1088 | } else { |
michael@0 | 1089 | doc->SetDisplayDocument(aDisplayDocument); |
michael@0 | 1090 | |
michael@0 | 1091 | // Make sure that hiding our viewer will tear down its presentation. |
michael@0 | 1092 | aViewer->SetSticky(false); |
michael@0 | 1093 | |
michael@0 | 1094 | rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0)); |
michael@0 | 1095 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 1096 | rv = aViewer->Open(nullptr, nullptr); |
michael@0 | 1097 | } |
michael@0 | 1098 | } |
michael@0 | 1099 | |
michael@0 | 1100 | if (NS_FAILED(rv)) { |
michael@0 | 1101 | doc = nullptr; |
michael@0 | 1102 | aViewer = nullptr; |
michael@0 | 1103 | aLoadGroup = nullptr; |
michael@0 | 1104 | } |
michael@0 | 1105 | } |
michael@0 | 1106 | |
michael@0 | 1107 | ExternalResource* newResource = new ExternalResource(); |
michael@0 | 1108 | mMap.Put(aURI, newResource); |
michael@0 | 1109 | |
michael@0 | 1110 | newResource->mDocument = doc; |
michael@0 | 1111 | newResource->mViewer = aViewer; |
michael@0 | 1112 | newResource->mLoadGroup = aLoadGroup; |
michael@0 | 1113 | if (doc) { |
michael@0 | 1114 | TransferZoomLevels(aDisplayDocument, doc); |
michael@0 | 1115 | TransferShowingState(aDisplayDocument, doc); |
michael@0 | 1116 | } |
michael@0 | 1117 | |
michael@0 | 1118 | const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers(); |
michael@0 | 1119 | for (uint32_t i = 0; i < obs.Length(); ++i) { |
michael@0 | 1120 | obs[i]->Observe(doc, "external-resource-document-created", nullptr); |
michael@0 | 1121 | } |
michael@0 | 1122 | |
michael@0 | 1123 | return rv; |
michael@0 | 1124 | } |
michael@0 | 1125 | |
michael@0 | 1126 | NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad, |
michael@0 | 1127 | nsIStreamListener, |
michael@0 | 1128 | nsIRequestObserver) |
michael@0 | 1129 | |
michael@0 | 1130 | NS_IMETHODIMP |
michael@0 | 1131 | nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest, |
michael@0 | 1132 | nsISupports *aContext) |
michael@0 | 1133 | { |
michael@0 | 1134 | nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap(); |
michael@0 | 1135 | if (map.HaveShutDown()) { |
michael@0 | 1136 | return NS_BINDING_ABORTED; |
michael@0 | 1137 | } |
michael@0 | 1138 | |
michael@0 | 1139 | nsCOMPtr<nsIContentViewer> viewer; |
michael@0 | 1140 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 1141 | nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer), |
michael@0 | 1142 | getter_AddRefs(loadGroup)); |
michael@0 | 1143 | |
michael@0 | 1144 | // Make sure to do this no matter what |
michael@0 | 1145 | nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup, |
michael@0 | 1146 | mDisplayDocument); |
michael@0 | 1147 | if (NS_FAILED(rv)) { |
michael@0 | 1148 | return rv; |
michael@0 | 1149 | } |
michael@0 | 1150 | if (NS_FAILED(rv2)) { |
michael@0 | 1151 | mTargetListener = nullptr; |
michael@0 | 1152 | return rv2; |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | return mTargetListener->OnStartRequest(aRequest, aContext); |
michael@0 | 1156 | } |
michael@0 | 1157 | |
michael@0 | 1158 | nsresult |
michael@0 | 1159 | nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest, |
michael@0 | 1160 | nsIContentViewer** aViewer, |
michael@0 | 1161 | nsILoadGroup** aLoadGroup) |
michael@0 | 1162 | { |
michael@0 | 1163 | NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest"); |
michael@0 | 1164 | *aViewer = nullptr; |
michael@0 | 1165 | *aLoadGroup = nullptr; |
michael@0 | 1166 | |
michael@0 | 1167 | nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest)); |
michael@0 | 1168 | NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); |
michael@0 | 1169 | |
michael@0 | 1170 | nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest)); |
michael@0 | 1171 | if (httpChannel) { |
michael@0 | 1172 | bool requestSucceeded; |
michael@0 | 1173 | if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) || |
michael@0 | 1174 | !requestSucceeded) { |
michael@0 | 1175 | // Bail out on this load, since it looks like we have an HTTP error page |
michael@0 | 1176 | return NS_BINDING_ABORTED; |
michael@0 | 1177 | } |
michael@0 | 1178 | } |
michael@0 | 1179 | |
michael@0 | 1180 | nsAutoCString type; |
michael@0 | 1181 | chan->GetContentType(type); |
michael@0 | 1182 | |
michael@0 | 1183 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 1184 | chan->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 1185 | |
michael@0 | 1186 | // Give this document its own loadgroup |
michael@0 | 1187 | nsCOMPtr<nsILoadGroup> newLoadGroup = |
michael@0 | 1188 | do_CreateInstance(NS_LOADGROUP_CONTRACTID); |
michael@0 | 1189 | NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 1190 | newLoadGroup->SetLoadGroup(loadGroup); |
michael@0 | 1191 | |
michael@0 | 1192 | nsCOMPtr<nsIInterfaceRequestor> callbacks; |
michael@0 | 1193 | loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); |
michael@0 | 1194 | |
michael@0 | 1195 | nsCOMPtr<nsIInterfaceRequestor> newCallbacks = |
michael@0 | 1196 | new LoadgroupCallbacks(callbacks); |
michael@0 | 1197 | newLoadGroup->SetNotificationCallbacks(newCallbacks); |
michael@0 | 1198 | |
michael@0 | 1199 | // This is some serious hackery cribbed from docshell |
michael@0 | 1200 | nsCOMPtr<nsICategoryManager> catMan = |
michael@0 | 1201 | do_GetService(NS_CATEGORYMANAGER_CONTRACTID); |
michael@0 | 1202 | NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE); |
michael@0 | 1203 | nsXPIDLCString contractId; |
michael@0 | 1204 | nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(), |
michael@0 | 1205 | getter_Copies(contractId)); |
michael@0 | 1206 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1207 | nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = |
michael@0 | 1208 | do_GetService(contractId); |
michael@0 | 1209 | NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); |
michael@0 | 1210 | |
michael@0 | 1211 | nsCOMPtr<nsIContentViewer> viewer; |
michael@0 | 1212 | nsCOMPtr<nsIStreamListener> listener; |
michael@0 | 1213 | rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup, |
michael@0 | 1214 | type.get(), nullptr, nullptr, |
michael@0 | 1215 | getter_AddRefs(listener), |
michael@0 | 1216 | getter_AddRefs(viewer)); |
michael@0 | 1217 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1218 | NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED); |
michael@0 | 1219 | |
michael@0 | 1220 | nsCOMPtr<nsIParser> parser = do_QueryInterface(listener); |
michael@0 | 1221 | if (!parser) { |
michael@0 | 1222 | /// We don't want to deal with the various fake documents yet |
michael@0 | 1223 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | // We can't handle HTML and other weird things here yet. |
michael@0 | 1227 | nsIContentSink* sink = parser->GetContentSink(); |
michael@0 | 1228 | nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink); |
michael@0 | 1229 | if (!xmlSink) { |
michael@0 | 1230 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 1231 | } |
michael@0 | 1232 | |
michael@0 | 1233 | listener.swap(mTargetListener); |
michael@0 | 1234 | viewer.forget(aViewer); |
michael@0 | 1235 | newLoadGroup.forget(aLoadGroup); |
michael@0 | 1236 | return NS_OK; |
michael@0 | 1237 | } |
michael@0 | 1238 | |
michael@0 | 1239 | NS_IMETHODIMP |
michael@0 | 1240 | nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest, |
michael@0 | 1241 | nsISupports* aContext, |
michael@0 | 1242 | nsIInputStream* aStream, |
michael@0 | 1243 | uint64_t aOffset, |
michael@0 | 1244 | uint32_t aCount) |
michael@0 | 1245 | { |
michael@0 | 1246 | NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!"); |
michael@0 | 1247 | if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) { |
michael@0 | 1248 | return NS_BINDING_ABORTED; |
michael@0 | 1249 | } |
michael@0 | 1250 | return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, |
michael@0 | 1251 | aCount); |
michael@0 | 1252 | } |
michael@0 | 1253 | |
michael@0 | 1254 | NS_IMETHODIMP |
michael@0 | 1255 | nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest, |
michael@0 | 1256 | nsISupports* aContext, |
michael@0 | 1257 | nsresult aStatus) |
michael@0 | 1258 | { |
michael@0 | 1259 | // mTargetListener might be null if SetupViewer or AddExternalResource failed |
michael@0 | 1260 | if (mTargetListener) { |
michael@0 | 1261 | nsCOMPtr<nsIStreamListener> listener; |
michael@0 | 1262 | mTargetListener.swap(listener); |
michael@0 | 1263 | return listener->OnStopRequest(aRequest, aContext, aStatus); |
michael@0 | 1264 | } |
michael@0 | 1265 | |
michael@0 | 1266 | return NS_OK; |
michael@0 | 1267 | } |
michael@0 | 1268 | |
michael@0 | 1269 | nsresult |
michael@0 | 1270 | nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI, |
michael@0 | 1271 | nsINode* aRequestingNode) |
michael@0 | 1272 | { |
michael@0 | 1273 | NS_PRECONDITION(aURI, "Must have a URI"); |
michael@0 | 1274 | NS_PRECONDITION(aRequestingNode, "Must have a node"); |
michael@0 | 1275 | |
michael@0 | 1276 | // Time to start a load. First, the security checks. |
michael@0 | 1277 | |
michael@0 | 1278 | nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal(); |
michael@0 | 1279 | |
michael@0 | 1280 | nsresult rv = nsContentUtils::GetSecurityManager()-> |
michael@0 | 1281 | CheckLoadURIWithPrincipal(requestingPrincipal, aURI, |
michael@0 | 1282 | nsIScriptSecurityManager::STANDARD); |
michael@0 | 1283 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1284 | |
michael@0 | 1285 | // Allow data URIs and other URI's that inherit their principal by passing |
michael@0 | 1286 | // true as the 3rd argument of CheckMayLoad, since we want |
michael@0 | 1287 | // to allow external resources from data URIs regardless of the difference |
michael@0 | 1288 | // in URI scheme. |
michael@0 | 1289 | rv = requestingPrincipal->CheckMayLoad(aURI, true, true); |
michael@0 | 1290 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1291 | |
michael@0 | 1292 | int16_t shouldLoad = nsIContentPolicy::ACCEPT; |
michael@0 | 1293 | rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER, |
michael@0 | 1294 | aURI, |
michael@0 | 1295 | requestingPrincipal, |
michael@0 | 1296 | aRequestingNode, |
michael@0 | 1297 | EmptyCString(), //mime guess |
michael@0 | 1298 | nullptr, //extra |
michael@0 | 1299 | &shouldLoad, |
michael@0 | 1300 | nsContentUtils::GetContentPolicy(), |
michael@0 | 1301 | nsContentUtils::GetSecurityManager()); |
michael@0 | 1302 | if (NS_FAILED(rv)) return rv; |
michael@0 | 1303 | if (NS_CP_REJECTED(shouldLoad)) { |
michael@0 | 1304 | // Disallowed by content policy |
michael@0 | 1305 | return NS_ERROR_CONTENT_BLOCKED; |
michael@0 | 1306 | } |
michael@0 | 1307 | |
michael@0 | 1308 | nsIDocument* doc = aRequestingNode->OwnerDoc(); |
michael@0 | 1309 | |
michael@0 | 1310 | nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker(); |
michael@0 | 1311 | NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 1312 | |
michael@0 | 1313 | nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup(); |
michael@0 | 1314 | nsCOMPtr<nsIChannel> channel; |
michael@0 | 1315 | rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, loadGroup, req); |
michael@0 | 1316 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1317 | |
michael@0 | 1318 | mURI = aURI; |
michael@0 | 1319 | |
michael@0 | 1320 | return channel->AsyncOpen(this, nullptr); |
michael@0 | 1321 | } |
michael@0 | 1322 | |
michael@0 | 1323 | NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks, |
michael@0 | 1324 | nsIInterfaceRequestor) |
michael@0 | 1325 | |
michael@0 | 1326 | #define IMPL_SHIM(_i) \ |
michael@0 | 1327 | NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i) |
michael@0 | 1328 | |
michael@0 | 1329 | IMPL_SHIM(nsILoadContext) |
michael@0 | 1330 | IMPL_SHIM(nsIProgressEventSink) |
michael@0 | 1331 | IMPL_SHIM(nsIChannelEventSink) |
michael@0 | 1332 | IMPL_SHIM(nsISecurityEventSink) |
michael@0 | 1333 | IMPL_SHIM(nsIApplicationCacheContainer) |
michael@0 | 1334 | |
michael@0 | 1335 | #undef IMPL_SHIM |
michael@0 | 1336 | |
michael@0 | 1337 | #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i)) |
michael@0 | 1338 | |
michael@0 | 1339 | #define TRY_SHIM(_i) \ |
michael@0 | 1340 | PR_BEGIN_MACRO \ |
michael@0 | 1341 | if (IID_IS(_i)) { \ |
michael@0 | 1342 | nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \ |
michael@0 | 1343 | if (!real) { \ |
michael@0 | 1344 | return NS_NOINTERFACE; \ |
michael@0 | 1345 | } \ |
michael@0 | 1346 | nsCOMPtr<_i> shim = new _i##Shim(this, real); \ |
michael@0 | 1347 | if (!shim) { \ |
michael@0 | 1348 | return NS_ERROR_OUT_OF_MEMORY; \ |
michael@0 | 1349 | } \ |
michael@0 | 1350 | shim.forget(aSink); \ |
michael@0 | 1351 | return NS_OK; \ |
michael@0 | 1352 | } \ |
michael@0 | 1353 | PR_END_MACRO |
michael@0 | 1354 | |
michael@0 | 1355 | NS_IMETHODIMP |
michael@0 | 1356 | nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID, |
michael@0 | 1357 | void **aSink) |
michael@0 | 1358 | { |
michael@0 | 1359 | if (mCallbacks && |
michael@0 | 1360 | (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) || |
michael@0 | 1361 | IID_IS(nsITabChild))) { |
michael@0 | 1362 | return mCallbacks->GetInterface(aIID, aSink); |
michael@0 | 1363 | } |
michael@0 | 1364 | |
michael@0 | 1365 | *aSink = nullptr; |
michael@0 | 1366 | |
michael@0 | 1367 | TRY_SHIM(nsILoadContext); |
michael@0 | 1368 | TRY_SHIM(nsIProgressEventSink); |
michael@0 | 1369 | TRY_SHIM(nsIChannelEventSink); |
michael@0 | 1370 | TRY_SHIM(nsISecurityEventSink); |
michael@0 | 1371 | TRY_SHIM(nsIApplicationCacheContainer); |
michael@0 | 1372 | |
michael@0 | 1373 | return NS_NOINTERFACE; |
michael@0 | 1374 | } |
michael@0 | 1375 | |
michael@0 | 1376 | #undef TRY_SHIM |
michael@0 | 1377 | #undef IID_IS |
michael@0 | 1378 | |
michael@0 | 1379 | nsExternalResourceMap::ExternalResource::~ExternalResource() |
michael@0 | 1380 | { |
michael@0 | 1381 | if (mViewer) { |
michael@0 | 1382 | mViewer->Close(nullptr); |
michael@0 | 1383 | mViewer->Destroy(); |
michael@0 | 1384 | } |
michael@0 | 1385 | } |
michael@0 | 1386 | |
michael@0 | 1387 | // ================================================================== |
michael@0 | 1388 | // = |
michael@0 | 1389 | // ================================================================== |
michael@0 | 1390 | |
michael@0 | 1391 | // If we ever have an nsIDocumentObserver notification for stylesheet title |
michael@0 | 1392 | // changes we should update the list from that instead of overriding |
michael@0 | 1393 | // EnsureFresh. |
michael@0 | 1394 | class nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList |
michael@0 | 1395 | { |
michael@0 | 1396 | public: |
michael@0 | 1397 | nsDOMStyleSheetSetList(nsIDocument* aDocument); |
michael@0 | 1398 | |
michael@0 | 1399 | void Disconnect() |
michael@0 | 1400 | { |
michael@0 | 1401 | mDocument = nullptr; |
michael@0 | 1402 | } |
michael@0 | 1403 | |
michael@0 | 1404 | virtual void EnsureFresh() MOZ_OVERRIDE; |
michael@0 | 1405 | |
michael@0 | 1406 | protected: |
michael@0 | 1407 | nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it |
michael@0 | 1408 | // dies. |
michael@0 | 1409 | }; |
michael@0 | 1410 | |
michael@0 | 1411 | nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument) |
michael@0 | 1412 | : mDocument(aDocument) |
michael@0 | 1413 | { |
michael@0 | 1414 | NS_ASSERTION(mDocument, "Must have document!"); |
michael@0 | 1415 | } |
michael@0 | 1416 | |
michael@0 | 1417 | void |
michael@0 | 1418 | nsDOMStyleSheetSetList::EnsureFresh() |
michael@0 | 1419 | { |
michael@0 | 1420 | mNames.Clear(); |
michael@0 | 1421 | |
michael@0 | 1422 | if (!mDocument) { |
michael@0 | 1423 | return; // Spec says "no exceptions", and we have no style sets if we have |
michael@0 | 1424 | // no document, for sure |
michael@0 | 1425 | } |
michael@0 | 1426 | |
michael@0 | 1427 | int32_t count = mDocument->GetNumberOfStyleSheets(); |
michael@0 | 1428 | nsAutoString title; |
michael@0 | 1429 | for (int32_t index = 0; index < count; index++) { |
michael@0 | 1430 | nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index); |
michael@0 | 1431 | NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
michael@0 | 1432 | sheet->GetTitle(title); |
michael@0 | 1433 | if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) { |
michael@0 | 1434 | return; |
michael@0 | 1435 | } |
michael@0 | 1436 | } |
michael@0 | 1437 | } |
michael@0 | 1438 | |
michael@0 | 1439 | // ================================================================== |
michael@0 | 1440 | nsIDocument::SelectorCache::SelectorCache() |
michael@0 | 1441 | : nsExpirationTracker<SelectorCacheKey, 4>(1000) { } |
michael@0 | 1442 | |
michael@0 | 1443 | // CacheList takes ownership of aSelectorList. |
michael@0 | 1444 | void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector, |
michael@0 | 1445 | nsCSSSelectorList* aSelectorList) |
michael@0 | 1446 | { |
michael@0 | 1447 | SelectorCacheKey* key = new SelectorCacheKey(aSelector); |
michael@0 | 1448 | mTable.Put(key->mKey, aSelectorList); |
michael@0 | 1449 | AddObject(key); |
michael@0 | 1450 | } |
michael@0 | 1451 | |
michael@0 | 1452 | class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable |
michael@0 | 1453 | { |
michael@0 | 1454 | public: |
michael@0 | 1455 | explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete) |
michael@0 | 1456 | : mSelector(aToDelete) |
michael@0 | 1457 | { |
michael@0 | 1458 | MOZ_COUNT_CTOR(SelectorCacheKeyDeleter); |
michael@0 | 1459 | } |
michael@0 | 1460 | |
michael@0 | 1461 | ~SelectorCacheKeyDeleter() |
michael@0 | 1462 | { |
michael@0 | 1463 | MOZ_COUNT_DTOR(SelectorCacheKeyDeleter); |
michael@0 | 1464 | } |
michael@0 | 1465 | |
michael@0 | 1466 | NS_IMETHOD Run() |
michael@0 | 1467 | { |
michael@0 | 1468 | return NS_OK; |
michael@0 | 1469 | } |
michael@0 | 1470 | |
michael@0 | 1471 | private: |
michael@0 | 1472 | nsAutoPtr<SelectorCacheKey> mSelector; |
michael@0 | 1473 | }; |
michael@0 | 1474 | |
michael@0 | 1475 | void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector) |
michael@0 | 1476 | { |
michael@0 | 1477 | RemoveObject(aSelector); |
michael@0 | 1478 | mTable.Remove(aSelector->mKey); |
michael@0 | 1479 | nsCOMPtr<nsIRunnable> runnable = new SelectorCacheKeyDeleter(aSelector); |
michael@0 | 1480 | NS_DispatchToCurrentThread(runnable); |
michael@0 | 1481 | } |
michael@0 | 1482 | |
michael@0 | 1483 | |
michael@0 | 1484 | struct nsIDocument::FrameRequest |
michael@0 | 1485 | { |
michael@0 | 1486 | FrameRequest(const FrameRequestCallbackHolder& aCallback, |
michael@0 | 1487 | int32_t aHandle) : |
michael@0 | 1488 | mCallback(aCallback), |
michael@0 | 1489 | mHandle(aHandle) |
michael@0 | 1490 | {} |
michael@0 | 1491 | |
michael@0 | 1492 | // Conversion operator so that we can append these to a |
michael@0 | 1493 | // FrameRequestCallbackList |
michael@0 | 1494 | operator const FrameRequestCallbackHolder& () const { |
michael@0 | 1495 | return mCallback; |
michael@0 | 1496 | } |
michael@0 | 1497 | |
michael@0 | 1498 | // Comparator operators to allow RemoveElementSorted with an |
michael@0 | 1499 | // integer argument on arrays of FrameRequest |
michael@0 | 1500 | bool operator==(int32_t aHandle) const { |
michael@0 | 1501 | return mHandle == aHandle; |
michael@0 | 1502 | } |
michael@0 | 1503 | bool operator<(int32_t aHandle) const { |
michael@0 | 1504 | return mHandle < aHandle; |
michael@0 | 1505 | } |
michael@0 | 1506 | |
michael@0 | 1507 | FrameRequestCallbackHolder mCallback; |
michael@0 | 1508 | int32_t mHandle; |
michael@0 | 1509 | }; |
michael@0 | 1510 | |
michael@0 | 1511 | static already_AddRefed<nsINodeInfo> nullNodeInfo(nullptr); |
michael@0 | 1512 | |
michael@0 | 1513 | // ================================================================== |
michael@0 | 1514 | // = |
michael@0 | 1515 | // ================================================================== |
michael@0 | 1516 | nsIDocument::nsIDocument() |
michael@0 | 1517 | : nsINode(nullNodeInfo), |
michael@0 | 1518 | mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")), |
michael@0 | 1519 | mNodeInfoManager(nullptr), |
michael@0 | 1520 | mCompatMode(eCompatibility_FullStandards), |
michael@0 | 1521 | mVisibilityState(dom::VisibilityState::Hidden), |
michael@0 | 1522 | mIsInitialDocumentInWindow(false), |
michael@0 | 1523 | mMayStartLayout(true), |
michael@0 | 1524 | mVisible(true), |
michael@0 | 1525 | mRemovedFromDocShell(false), |
michael@0 | 1526 | // mAllowDNSPrefetch starts true, so that we can always reliably && it |
michael@0 | 1527 | // with various values that might disable it. Since we never prefetch |
michael@0 | 1528 | // unless we get a window, and in that case the docshell value will get |
michael@0 | 1529 | // &&-ed in, this is safe. |
michael@0 | 1530 | mAllowDNSPrefetch(true), |
michael@0 | 1531 | mIsBeingUsedAsImage(false), |
michael@0 | 1532 | mHasLinksToUpdate(false), |
michael@0 | 1533 | mPartID(0), |
michael@0 | 1534 | mDidFireDOMContentLoaded(true) |
michael@0 | 1535 | { |
michael@0 | 1536 | SetInDocument(); |
michael@0 | 1537 | } |
michael@0 | 1538 | |
michael@0 | 1539 | // NOTE! nsDocument::operator new() zeroes out all members, so don't |
michael@0 | 1540 | // bother initializing members to 0. |
michael@0 | 1541 | |
michael@0 | 1542 | nsDocument::nsDocument(const char* aContentType) |
michael@0 | 1543 | : nsIDocument() |
michael@0 | 1544 | , mAnimatingImages(true) |
michael@0 | 1545 | , mViewportType(Unknown) |
michael@0 | 1546 | { |
michael@0 | 1547 | SetContentTypeInternal(nsDependentCString(aContentType)); |
michael@0 | 1548 | |
michael@0 | 1549 | #ifdef PR_LOGGING |
michael@0 | 1550 | if (!gDocumentLeakPRLog) |
michael@0 | 1551 | gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak"); |
michael@0 | 1552 | |
michael@0 | 1553 | if (gDocumentLeakPRLog) |
michael@0 | 1554 | PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, |
michael@0 | 1555 | ("DOCUMENT %p created", this)); |
michael@0 | 1556 | |
michael@0 | 1557 | if (!gCspPRLog) |
michael@0 | 1558 | gCspPRLog = PR_NewLogModule("CSP"); |
michael@0 | 1559 | #endif |
michael@0 | 1560 | |
michael@0 | 1561 | // Start out mLastStyleSheetSet as null, per spec |
michael@0 | 1562 | SetDOMStringToNull(mLastStyleSheetSet); |
michael@0 | 1563 | |
michael@0 | 1564 | if (sProcessingStack.empty()) { |
michael@0 | 1565 | sProcessingStack.construct(); |
michael@0 | 1566 | // Add the base queue sentinel to the processing stack. |
michael@0 | 1567 | sProcessingStack.ref().AppendElement((CustomElementData*) nullptr); |
michael@0 | 1568 | } |
michael@0 | 1569 | } |
michael@0 | 1570 | |
michael@0 | 1571 | static PLDHashOperator |
michael@0 | 1572 | ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg) |
michael@0 | 1573 | { |
michael@0 | 1574 | if (aBoxObject) { |
michael@0 | 1575 | aBoxObject->Clear(); |
michael@0 | 1576 | } |
michael@0 | 1577 | return PL_DHASH_NEXT; |
michael@0 | 1578 | } |
michael@0 | 1579 | |
michael@0 | 1580 | nsIDocument::~nsIDocument() |
michael@0 | 1581 | { |
michael@0 | 1582 | if (mNodeInfoManager) { |
michael@0 | 1583 | mNodeInfoManager->DropDocumentReference(); |
michael@0 | 1584 | } |
michael@0 | 1585 | } |
michael@0 | 1586 | |
michael@0 | 1587 | |
michael@0 | 1588 | nsDocument::~nsDocument() |
michael@0 | 1589 | { |
michael@0 | 1590 | #ifdef PR_LOGGING |
michael@0 | 1591 | if (gDocumentLeakPRLog) |
michael@0 | 1592 | PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, |
michael@0 | 1593 | ("DOCUMENT %p destroyed", this)); |
michael@0 | 1594 | #endif |
michael@0 | 1595 | |
michael@0 | 1596 | NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document"); |
michael@0 | 1597 | |
michael@0 | 1598 | if (IsTopLevelContentDocument()) { |
michael@0 | 1599 | //don't report for about: pages |
michael@0 | 1600 | nsCOMPtr<nsIPrincipal> principal = GetPrincipal(); |
michael@0 | 1601 | nsCOMPtr<nsIURI> uri; |
michael@0 | 1602 | principal->GetURI(getter_AddRefs(uri)); |
michael@0 | 1603 | bool isAboutScheme = true; |
michael@0 | 1604 | if (uri) { |
michael@0 | 1605 | uri->SchemeIs("about", &isAboutScheme); |
michael@0 | 1606 | } |
michael@0 | 1607 | |
michael@0 | 1608 | if (!isAboutScheme) { |
michael@0 | 1609 | // Record the page load |
michael@0 | 1610 | uint32_t pageLoaded = 1; |
michael@0 | 1611 | Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded); |
michael@0 | 1612 | // Record the mixed content status of the docshell in Telemetry |
michael@0 | 1613 | enum { |
michael@0 | 1614 | NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page |
michael@0 | 1615 | MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content |
michael@0 | 1616 | MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content |
michael@0 | 1617 | MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content |
michael@0 | 1618 | }; |
michael@0 | 1619 | |
michael@0 | 1620 | bool mixedActiveLoaded = GetHasMixedActiveContentLoaded(); |
michael@0 | 1621 | bool mixedActiveBlocked = GetHasMixedActiveContentBlocked(); |
michael@0 | 1622 | |
michael@0 | 1623 | bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded(); |
michael@0 | 1624 | bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked(); |
michael@0 | 1625 | |
michael@0 | 1626 | bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded); |
michael@0 | 1627 | bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded); |
michael@0 | 1628 | |
michael@0 | 1629 | uint32_t mixedContentLevel = NO_MIXED_CONTENT; |
michael@0 | 1630 | if (hasMixedDisplay && hasMixedActive) { |
michael@0 | 1631 | mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT; |
michael@0 | 1632 | } else if (hasMixedActive){ |
michael@0 | 1633 | mixedContentLevel = MIXED_ACTIVE_CONTENT; |
michael@0 | 1634 | } else if (hasMixedDisplay) { |
michael@0 | 1635 | mixedContentLevel = MIXED_DISPLAY_CONTENT; |
michael@0 | 1636 | } |
michael@0 | 1637 | Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel); |
michael@0 | 1638 | } |
michael@0 | 1639 | } |
michael@0 | 1640 | |
michael@0 | 1641 | mInDestructor = true; |
michael@0 | 1642 | mInUnlinkOrDeletion = true; |
michael@0 | 1643 | |
michael@0 | 1644 | mRegistry = nullptr; |
michael@0 | 1645 | |
michael@0 | 1646 | mozilla::DropJSObjects(this); |
michael@0 | 1647 | |
michael@0 | 1648 | // Clear mObservers to keep it in sync with the mutationobserver list |
michael@0 | 1649 | mObservers.Clear(); |
michael@0 | 1650 | |
michael@0 | 1651 | if (mStyleSheetSetList) { |
michael@0 | 1652 | mStyleSheetSetList->Disconnect(); |
michael@0 | 1653 | } |
michael@0 | 1654 | |
michael@0 | 1655 | if (mAnimationController) { |
michael@0 | 1656 | mAnimationController->Disconnect(); |
michael@0 | 1657 | } |
michael@0 | 1658 | |
michael@0 | 1659 | mParentDocument = nullptr; |
michael@0 | 1660 | |
michael@0 | 1661 | // Kill the subdocument map, doing this will release its strong |
michael@0 | 1662 | // references, if any. |
michael@0 | 1663 | if (mSubDocuments) { |
michael@0 | 1664 | PL_DHashTableDestroy(mSubDocuments); |
michael@0 | 1665 | |
michael@0 | 1666 | mSubDocuments = nullptr; |
michael@0 | 1667 | } |
michael@0 | 1668 | |
michael@0 | 1669 | // Destroy link map now so we don't waste time removing |
michael@0 | 1670 | // links one by one |
michael@0 | 1671 | DestroyElementMaps(); |
michael@0 | 1672 | |
michael@0 | 1673 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 1674 | |
michael@0 | 1675 | int32_t indx; // must be signed |
michael@0 | 1676 | uint32_t count = mChildren.ChildCount(); |
michael@0 | 1677 | for (indx = int32_t(count) - 1; indx >= 0; --indx) { |
michael@0 | 1678 | mChildren.ChildAt(indx)->UnbindFromTree(); |
michael@0 | 1679 | mChildren.RemoveChildAt(indx); |
michael@0 | 1680 | } |
michael@0 | 1681 | mFirstChild = nullptr; |
michael@0 | 1682 | mCachedRootElement = nullptr; |
michael@0 | 1683 | |
michael@0 | 1684 | // Let the stylesheets know we're going away |
michael@0 | 1685 | indx = mStyleSheets.Count(); |
michael@0 | 1686 | while (--indx >= 0) { |
michael@0 | 1687 | mStyleSheets[indx]->SetOwningDocument(nullptr); |
michael@0 | 1688 | } |
michael@0 | 1689 | indx = mCatalogSheets.Count(); |
michael@0 | 1690 | while (--indx >= 0) { |
michael@0 | 1691 | static_cast<nsCSSStyleSheet*>(mCatalogSheets[indx])->SetOwningNode(nullptr); |
michael@0 | 1692 | mCatalogSheets[indx]->SetOwningDocument(nullptr); |
michael@0 | 1693 | } |
michael@0 | 1694 | if (mAttrStyleSheet) { |
michael@0 | 1695 | mAttrStyleSheet->SetOwningDocument(nullptr); |
michael@0 | 1696 | } |
michael@0 | 1697 | |
michael@0 | 1698 | if (mListenerManager) { |
michael@0 | 1699 | mListenerManager->Disconnect(); |
michael@0 | 1700 | UnsetFlags(NODE_HAS_LISTENERMANAGER); |
michael@0 | 1701 | } |
michael@0 | 1702 | |
michael@0 | 1703 | if (mScriptLoader) { |
michael@0 | 1704 | mScriptLoader->DropDocumentReference(); |
michael@0 | 1705 | } |
michael@0 | 1706 | |
michael@0 | 1707 | if (mCSSLoader) { |
michael@0 | 1708 | // Could be null here if Init() failed |
michael@0 | 1709 | mCSSLoader->DropDocumentReference(); |
michael@0 | 1710 | } |
michael@0 | 1711 | |
michael@0 | 1712 | if (mStyleImageLoader) { |
michael@0 | 1713 | mStyleImageLoader->DropDocumentReference(); |
michael@0 | 1714 | } |
michael@0 | 1715 | |
michael@0 | 1716 | delete mHeaderData; |
michael@0 | 1717 | |
michael@0 | 1718 | if (mBoxObjectTable) { |
michael@0 | 1719 | mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr); |
michael@0 | 1720 | delete mBoxObjectTable; |
michael@0 | 1721 | } |
michael@0 | 1722 | |
michael@0 | 1723 | mPendingTitleChangeEvent.Revoke(); |
michael@0 | 1724 | |
michael@0 | 1725 | for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) { |
michael@0 | 1726 | nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]); |
michael@0 | 1727 | } |
michael@0 | 1728 | |
michael@0 | 1729 | // We don't want to leave residual locks on images. Make sure we're in an |
michael@0 | 1730 | // unlocked state, and then clear the table. |
michael@0 | 1731 | SetImageLockingState(false); |
michael@0 | 1732 | mImageTracker.Clear(); |
michael@0 | 1733 | |
michael@0 | 1734 | mPlugins.Clear(); |
michael@0 | 1735 | } |
michael@0 | 1736 | |
michael@0 | 1737 | NS_INTERFACE_TABLE_HEAD(nsDocument) |
michael@0 | 1738 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
michael@0 | 1739 | NS_INTERFACE_TABLE_BEGIN |
michael@0 | 1740 | NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode) |
michael@0 | 1741 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode) |
michael@0 | 1742 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument) |
michael@0 | 1743 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument) |
michael@0 | 1744 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode) |
michael@0 | 1745 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL) |
michael@0 | 1746 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal) |
michael@0 | 1747 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget) |
michael@0 | 1748 | NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget) |
michael@0 | 1749 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference) |
michael@0 | 1750 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer) |
michael@0 | 1751 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver) |
michael@0 | 1752 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer) |
michael@0 | 1753 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver) |
michael@0 | 1754 | NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator) |
michael@0 | 1755 | NS_INTERFACE_TABLE_END |
michael@0 | 1756 | NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument) |
michael@0 | 1757 | NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver, |
michael@0 | 1758 | new nsNode3Tearoff(this)) |
michael@0 | 1759 | NS_INTERFACE_MAP_END |
michael@0 | 1760 | |
michael@0 | 1761 | |
michael@0 | 1762 | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument) |
michael@0 | 1763 | NS_IMETHODIMP_(MozExternalRefCountType) |
michael@0 | 1764 | nsDocument::Release() |
michael@0 | 1765 | { |
michael@0 | 1766 | NS_PRECONDITION(0 != mRefCnt, "dup release"); |
michael@0 | 1767 | NS_ASSERT_OWNINGTHREAD(nsDocument); |
michael@0 | 1768 | nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this); |
michael@0 | 1769 | bool shouldDelete = false; |
michael@0 | 1770 | nsrefcnt count = mRefCnt.decr(base, &shouldDelete); |
michael@0 | 1771 | NS_LOG_RELEASE(this, count, "nsDocument"); |
michael@0 | 1772 | if (count == 0) { |
michael@0 | 1773 | if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) { |
michael@0 | 1774 | mNeedsReleaseAfterStackRefCntRelease = true; |
michael@0 | 1775 | NS_ADDREF_THIS(); |
michael@0 | 1776 | return mRefCnt.get(); |
michael@0 | 1777 | } |
michael@0 | 1778 | mRefCnt.incr(base); |
michael@0 | 1779 | nsNodeUtils::LastRelease(this); |
michael@0 | 1780 | mRefCnt.decr(base); |
michael@0 | 1781 | if (shouldDelete) { |
michael@0 | 1782 | mRefCnt.stabilizeForDeletion(); |
michael@0 | 1783 | DeleteCycleCollectable(); |
michael@0 | 1784 | } |
michael@0 | 1785 | } |
michael@0 | 1786 | return count; |
michael@0 | 1787 | } |
michael@0 | 1788 | |
michael@0 | 1789 | NS_IMETHODIMP_(void) |
michael@0 | 1790 | nsDocument::DeleteCycleCollectable() |
michael@0 | 1791 | { |
michael@0 | 1792 | delete this; |
michael@0 | 1793 | } |
michael@0 | 1794 | |
michael@0 | 1795 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument) |
michael@0 | 1796 | if (Element::CanSkip(tmp, aRemovingAllowed)) { |
michael@0 | 1797 | EventListenerManager* elm = tmp->GetExistingListenerManager(); |
michael@0 | 1798 | if (elm) { |
michael@0 | 1799 | elm->MarkForCC(); |
michael@0 | 1800 | } |
michael@0 | 1801 | return true; |
michael@0 | 1802 | } |
michael@0 | 1803 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
michael@0 | 1804 | |
michael@0 | 1805 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument) |
michael@0 | 1806 | return Element::CanSkipInCC(tmp); |
michael@0 | 1807 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
michael@0 | 1808 | |
michael@0 | 1809 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument) |
michael@0 | 1810 | return Element::CanSkipThis(tmp); |
michael@0 | 1811 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
michael@0 | 1812 | |
michael@0 | 1813 | static PLDHashOperator |
michael@0 | 1814 | SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, |
michael@0 | 1815 | void *arg) |
michael@0 | 1816 | { |
michael@0 | 1817 | SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
michael@0 | 1818 | nsCycleCollectionTraversalCallback *cb = |
michael@0 | 1819 | static_cast<nsCycleCollectionTraversalCallback*>(arg); |
michael@0 | 1820 | |
michael@0 | 1821 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey"); |
michael@0 | 1822 | cb->NoteXPCOMChild(entry->mKey); |
michael@0 | 1823 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument"); |
michael@0 | 1824 | cb->NoteXPCOMChild(entry->mSubDocument); |
michael@0 | 1825 | |
michael@0 | 1826 | return PL_DHASH_NEXT; |
michael@0 | 1827 | } |
michael@0 | 1828 | |
michael@0 | 1829 | static PLDHashOperator |
michael@0 | 1830 | RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData, |
michael@0 | 1831 | void* aClosure) |
michael@0 | 1832 | { |
michael@0 | 1833 | nsCycleCollectionTraversalCallback *cb = |
michael@0 | 1834 | static_cast<nsCycleCollectionTraversalCallback*>(aClosure); |
michael@0 | 1835 | |
michael@0 | 1836 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 1837 | "mRadioGroups entry->mSelectedRadioButton"); |
michael@0 | 1838 | cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton)); |
michael@0 | 1839 | |
michael@0 | 1840 | uint32_t i, count = aData->mRadioButtons.Count(); |
michael@0 | 1841 | for (i = 0; i < count; ++i) { |
michael@0 | 1842 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, |
michael@0 | 1843 | "mRadioGroups entry->mRadioButtons[i]"); |
michael@0 | 1844 | cb->NoteXPCOMChild(aData->mRadioButtons[i]); |
michael@0 | 1845 | } |
michael@0 | 1846 | |
michael@0 | 1847 | return PL_DHASH_NEXT; |
michael@0 | 1848 | } |
michael@0 | 1849 | |
michael@0 | 1850 | static PLDHashOperator |
michael@0 | 1851 | BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg) |
michael@0 | 1852 | { |
michael@0 | 1853 | nsCycleCollectionTraversalCallback *cb = |
michael@0 | 1854 | static_cast<nsCycleCollectionTraversalCallback*>(userArg); |
michael@0 | 1855 | |
michael@0 | 1856 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry"); |
michael@0 | 1857 | cb->NoteXPCOMChild(boxObject); |
michael@0 | 1858 | |
michael@0 | 1859 | return PL_DHASH_NEXT; |
michael@0 | 1860 | } |
michael@0 | 1861 | |
michael@0 | 1862 | static PLDHashOperator |
michael@0 | 1863 | IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg) |
michael@0 | 1864 | { |
michael@0 | 1865 | nsCycleCollectionTraversalCallback *cb = |
michael@0 | 1866 | static_cast<nsCycleCollectionTraversalCallback*>(aArg); |
michael@0 | 1867 | aEntry->Traverse(cb); |
michael@0 | 1868 | return PL_DHASH_NEXT; |
michael@0 | 1869 | } |
michael@0 | 1870 | |
michael@0 | 1871 | static const char* kNSURIs[] = { |
michael@0 | 1872 | "([none])", |
michael@0 | 1873 | "(xmlns)", |
michael@0 | 1874 | "(xml)", |
michael@0 | 1875 | "(xhtml)", |
michael@0 | 1876 | "(XLink)", |
michael@0 | 1877 | "(XSLT)", |
michael@0 | 1878 | "(XBL)", |
michael@0 | 1879 | "(MathML)", |
michael@0 | 1880 | "(RDF)", |
michael@0 | 1881 | "(XUL)" |
michael@0 | 1882 | }; |
michael@0 | 1883 | |
michael@0 | 1884 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) |
michael@0 | 1885 | if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
michael@0 | 1886 | char name[512]; |
michael@0 | 1887 | nsAutoCString loadedAsData; |
michael@0 | 1888 | if (tmp->IsLoadedAsData()) { |
michael@0 | 1889 | loadedAsData.AssignLiteral("data"); |
michael@0 | 1890 | } else { |
michael@0 | 1891 | loadedAsData.AssignLiteral("normal"); |
michael@0 | 1892 | } |
michael@0 | 1893 | uint32_t nsid = tmp->GetDefaultNamespaceID(); |
michael@0 | 1894 | nsAutoCString uri; |
michael@0 | 1895 | if (tmp->mDocumentURI) |
michael@0 | 1896 | tmp->mDocumentURI->GetSpec(uri); |
michael@0 | 1897 | if (nsid < ArrayLength(kNSURIs)) { |
michael@0 | 1898 | PR_snprintf(name, sizeof(name), "nsDocument %s %s %s", |
michael@0 | 1899 | loadedAsData.get(), kNSURIs[nsid], uri.get()); |
michael@0 | 1900 | } |
michael@0 | 1901 | else { |
michael@0 | 1902 | PR_snprintf(name, sizeof(name), "nsDocument %s %s", |
michael@0 | 1903 | loadedAsData.get(), uri.get()); |
michael@0 | 1904 | } |
michael@0 | 1905 | cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
michael@0 | 1906 | } |
michael@0 | 1907 | else { |
michael@0 | 1908 | NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get()) |
michael@0 | 1909 | } |
michael@0 | 1910 | |
michael@0 | 1911 | // Always need to traverse script objects, so do that before we check |
michael@0 | 1912 | // if we're uncollectable. |
michael@0 | 1913 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
michael@0 | 1914 | |
michael@0 | 1915 | if (!nsINode::Traverse(tmp, cb)) { |
michael@0 | 1916 | return NS_SUCCESS_INTERRUPTED_TRAVERSE; |
michael@0 | 1917 | } |
michael@0 | 1918 | |
michael@0 | 1919 | tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb); |
michael@0 | 1920 | |
michael@0 | 1921 | tmp->mExternalResourceMap.Traverse(&cb); |
michael@0 | 1922 | |
michael@0 | 1923 | // Traverse the mChildren nsAttrAndChildArray. |
michael@0 | 1924 | for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) { |
michael@0 | 1925 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]"); |
michael@0 | 1926 | cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1)); |
michael@0 | 1927 | } |
michael@0 | 1928 | |
michael@0 | 1929 | // Traverse all nsIDocument pointer members. |
michael@0 | 1930 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo) |
michael@0 | 1931 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument) |
michael@0 | 1932 | |
michael@0 | 1933 | // Traverse all nsDocument nsCOMPtrs. |
michael@0 | 1934 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) |
michael@0 | 1935 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject) |
michael@0 | 1936 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) |
michael@0 | 1937 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets) |
michael@0 | 1938 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList) |
michael@0 | 1939 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) |
michael@0 | 1940 | |
michael@0 | 1941 | tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb); |
michael@0 | 1942 | |
michael@0 | 1943 | // The boxobject for an element will only exist as long as it's in the |
michael@0 | 1944 | // document, so we'll traverse the table here instead of from the element. |
michael@0 | 1945 | if (tmp->mBoxObjectTable) { |
michael@0 | 1946 | tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb); |
michael@0 | 1947 | } |
michael@0 | 1948 | |
michael@0 | 1949 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel) |
michael@0 | 1950 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet) |
michael@0 | 1951 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator) |
michael@0 | 1952 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState) |
michael@0 | 1953 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker) |
michael@0 | 1954 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref) |
michael@0 | 1955 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) |
michael@0 | 1956 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) |
michael@0 | 1957 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument) |
michael@0 | 1958 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder) |
michael@0 | 1959 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached) |
michael@0 | 1960 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager) |
michael@0 | 1961 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner) |
michael@0 | 1962 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection) |
michael@0 | 1963 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry) |
michael@0 | 1964 | |
michael@0 | 1965 | // Traverse all our nsCOMArrays. |
michael@0 | 1966 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets) |
michael@0 | 1967 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCatalogSheets) |
michael@0 | 1968 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages) |
michael@0 | 1969 | |
michael@0 | 1970 | for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) { |
michael@0 | 1971 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]"); |
michael@0 | 1972 | cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports()); |
michael@0 | 1973 | } |
michael@0 | 1974 | |
michael@0 | 1975 | // Traverse animation components |
michael@0 | 1976 | if (tmp->mAnimationController) { |
michael@0 | 1977 | tmp->mAnimationController->Traverse(&cb); |
michael@0 | 1978 | } |
michael@0 | 1979 | |
michael@0 | 1980 | if (tmp->mSubDocuments && tmp->mSubDocuments->ops) { |
michael@0 | 1981 | PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb); |
michael@0 | 1982 | } |
michael@0 | 1983 | |
michael@0 | 1984 | if (tmp->mCSSLoader) { |
michael@0 | 1985 | tmp->mCSSLoader->TraverseCachedSheets(cb); |
michael@0 | 1986 | } |
michael@0 | 1987 | |
michael@0 | 1988 | for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) { |
michael@0 | 1989 | nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb); |
michael@0 | 1990 | } |
michael@0 | 1991 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 1992 | |
michael@0 | 1993 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) |
michael@0 | 1994 | |
michael@0 | 1995 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument) |
michael@0 | 1996 | if (tmp->PreservingWrapper()) { |
michael@0 | 1997 | NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando); |
michael@0 | 1998 | } |
michael@0 | 1999 | NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
michael@0 | 2000 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
michael@0 | 2001 | |
michael@0 | 2002 | |
michael@0 | 2003 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) |
michael@0 | 2004 | tmp->mInUnlinkOrDeletion = true; |
michael@0 | 2005 | |
michael@0 | 2006 | // Clear out our external resources |
michael@0 | 2007 | tmp->mExternalResourceMap.Shutdown(); |
michael@0 | 2008 | |
michael@0 | 2009 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 2010 | |
michael@0 | 2011 | nsINode::Unlink(tmp); |
michael@0 | 2012 | |
michael@0 | 2013 | // Unlink the mChildren nsAttrAndChildArray. |
michael@0 | 2014 | for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1; |
michael@0 | 2015 | indx >= 0; --indx) { |
michael@0 | 2016 | tmp->mChildren.ChildAt(indx)->UnbindFromTree(); |
michael@0 | 2017 | tmp->mChildren.RemoveChildAt(indx); |
michael@0 | 2018 | } |
michael@0 | 2019 | tmp->mFirstChild = nullptr; |
michael@0 | 2020 | |
michael@0 | 2021 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator) |
michael@0 | 2022 | tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer |
michael@0 | 2023 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) |
michael@0 | 2024 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref) |
michael@0 | 2025 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation) |
michael@0 | 2026 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps) |
michael@0 | 2027 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument) |
michael@0 | 2028 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder) |
michael@0 | 2029 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager) |
michael@0 | 2030 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner) |
michael@0 | 2031 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection) |
michael@0 | 2032 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry) |
michael@0 | 2033 | |
michael@0 | 2034 | tmp->mParentDocument = nullptr; |
michael@0 | 2035 | |
michael@0 | 2036 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages) |
michael@0 | 2037 | |
michael@0 | 2038 | |
michael@0 | 2039 | if (tmp->mBoxObjectTable) { |
michael@0 | 2040 | tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr); |
michael@0 | 2041 | delete tmp->mBoxObjectTable; |
michael@0 | 2042 | tmp->mBoxObjectTable = nullptr; |
michael@0 | 2043 | } |
michael@0 | 2044 | |
michael@0 | 2045 | if (tmp->mListenerManager) { |
michael@0 | 2046 | tmp->mListenerManager->Disconnect(); |
michael@0 | 2047 | tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); |
michael@0 | 2048 | tmp->mListenerManager = nullptr; |
michael@0 | 2049 | } |
michael@0 | 2050 | |
michael@0 | 2051 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets) |
michael@0 | 2052 | |
michael@0 | 2053 | if (tmp->mStyleSheetSetList) { |
michael@0 | 2054 | tmp->mStyleSheetSetList->Disconnect(); |
michael@0 | 2055 | tmp->mStyleSheetSetList = nullptr; |
michael@0 | 2056 | } |
michael@0 | 2057 | |
michael@0 | 2058 | if (tmp->mSubDocuments) { |
michael@0 | 2059 | PL_DHashTableDestroy(tmp->mSubDocuments); |
michael@0 | 2060 | tmp->mSubDocuments = nullptr; |
michael@0 | 2061 | } |
michael@0 | 2062 | |
michael@0 | 2063 | tmp->mFrameRequestCallbacks.Clear(); |
michael@0 | 2064 | |
michael@0 | 2065 | tmp->mRadioGroups.Clear(); |
michael@0 | 2066 | |
michael@0 | 2067 | // nsDocument has a pretty complex destructor, so we're going to |
michael@0 | 2068 | // assume that *most* cycles you actually want to break somewhere |
michael@0 | 2069 | // else, and not unlink an awful lot here. |
michael@0 | 2070 | |
michael@0 | 2071 | tmp->mIdentifierMap.Clear(); |
michael@0 | 2072 | tmp->mExpandoAndGeneration.Unlink(); |
michael@0 | 2073 | |
michael@0 | 2074 | if (tmp->mAnimationController) { |
michael@0 | 2075 | tmp->mAnimationController->Unlink(); |
michael@0 | 2076 | } |
michael@0 | 2077 | |
michael@0 | 2078 | tmp->mPendingTitleChangeEvent.Revoke(); |
michael@0 | 2079 | |
michael@0 | 2080 | if (tmp->mCSSLoader) { |
michael@0 | 2081 | tmp->mCSSLoader->UnlinkCachedSheets(); |
michael@0 | 2082 | } |
michael@0 | 2083 | |
michael@0 | 2084 | for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) { |
michael@0 | 2085 | nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]); |
michael@0 | 2086 | } |
michael@0 | 2087 | |
michael@0 | 2088 | tmp->mInUnlinkOrDeletion = false; |
michael@0 | 2089 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 2090 | |
michael@0 | 2091 | static bool sPrefsInitialized = false; |
michael@0 | 2092 | static uint32_t sOnloadDecodeLimit = 0; |
michael@0 | 2093 | |
michael@0 | 2094 | nsresult |
michael@0 | 2095 | nsDocument::Init() |
michael@0 | 2096 | { |
michael@0 | 2097 | if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) { |
michael@0 | 2098 | return NS_ERROR_ALREADY_INITIALIZED; |
michael@0 | 2099 | } |
michael@0 | 2100 | |
michael@0 | 2101 | if (!sPrefsInitialized) { |
michael@0 | 2102 | sPrefsInitialized = true; |
michael@0 | 2103 | Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0); |
michael@0 | 2104 | } |
michael@0 | 2105 | |
michael@0 | 2106 | // Force initialization. |
michael@0 | 2107 | nsINode::nsSlots* slots = Slots(); |
michael@0 | 2108 | |
michael@0 | 2109 | // Prepend self as mutation-observer whether we need it or not (some |
michael@0 | 2110 | // subclasses currently do, other don't). This is because the code in |
michael@0 | 2111 | // nsNodeUtils always notifies the first observer first, expecting the |
michael@0 | 2112 | // first observer to be the document. |
michael@0 | 2113 | NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)), |
michael@0 | 2114 | NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 2115 | |
michael@0 | 2116 | |
michael@0 | 2117 | mOnloadBlocker = new nsOnloadBlocker(); |
michael@0 | 2118 | mCSSLoader = new mozilla::css::Loader(this); |
michael@0 | 2119 | // Assume we're not quirky, until we know otherwise |
michael@0 | 2120 | mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards); |
michael@0 | 2121 | |
michael@0 | 2122 | mStyleImageLoader = new mozilla::css::ImageLoader(this); |
michael@0 | 2123 | |
michael@0 | 2124 | mNodeInfoManager = new nsNodeInfoManager(); |
michael@0 | 2125 | nsresult rv = mNodeInfoManager->Init(this); |
michael@0 | 2126 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2127 | |
michael@0 | 2128 | // mNodeInfo keeps NodeInfoManager alive! |
michael@0 | 2129 | mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo(); |
michael@0 | 2130 | NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 2131 | NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE, |
michael@0 | 2132 | "Bad NodeType in aNodeInfo"); |
michael@0 | 2133 | |
michael@0 | 2134 | NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!"); |
michael@0 | 2135 | |
michael@0 | 2136 | // If after creation the owner js global is not set for a document |
michael@0 | 2137 | // we use the default compartment for this document, instead of creating |
michael@0 | 2138 | // wrapper in some random compartment when the document is exposed to js |
michael@0 | 2139 | // via some events. |
michael@0 | 2140 | nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal(); |
michael@0 | 2141 | NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); |
michael@0 | 2142 | mScopeObject = do_GetWeakReference(global); |
michael@0 | 2143 | MOZ_ASSERT(mScopeObject); |
michael@0 | 2144 | |
michael@0 | 2145 | mScriptLoader = new nsScriptLoader(this); |
michael@0 | 2146 | |
michael@0 | 2147 | mozilla::HoldJSObjects(this); |
michael@0 | 2148 | |
michael@0 | 2149 | return NS_OK; |
michael@0 | 2150 | } |
michael@0 | 2151 | |
michael@0 | 2152 | void |
michael@0 | 2153 | nsIDocument::DeleteAllProperties() |
michael@0 | 2154 | { |
michael@0 | 2155 | for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) { |
michael@0 | 2156 | PropertyTable(i)->DeleteAllProperties(); |
michael@0 | 2157 | } |
michael@0 | 2158 | } |
michael@0 | 2159 | |
michael@0 | 2160 | void |
michael@0 | 2161 | nsIDocument::DeleteAllPropertiesFor(nsINode* aNode) |
michael@0 | 2162 | { |
michael@0 | 2163 | for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) { |
michael@0 | 2164 | PropertyTable(i)->DeleteAllPropertiesFor(aNode); |
michael@0 | 2165 | } |
michael@0 | 2166 | } |
michael@0 | 2167 | |
michael@0 | 2168 | nsPropertyTable* |
michael@0 | 2169 | nsIDocument::GetExtraPropertyTable(uint16_t aCategory) |
michael@0 | 2170 | { |
michael@0 | 2171 | NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled"); |
michael@0 | 2172 | while (aCategory >= mExtraPropertyTables.Length() + 1) { |
michael@0 | 2173 | mExtraPropertyTables.AppendElement(new nsPropertyTable()); |
michael@0 | 2174 | } |
michael@0 | 2175 | return mExtraPropertyTables[aCategory - 1]; |
michael@0 | 2176 | } |
michael@0 | 2177 | |
michael@0 | 2178 | bool |
michael@0 | 2179 | nsIDocument::IsVisibleConsideringAncestors() const |
michael@0 | 2180 | { |
michael@0 | 2181 | const nsIDocument *parent = this; |
michael@0 | 2182 | do { |
michael@0 | 2183 | if (!parent->IsVisible()) { |
michael@0 | 2184 | return false; |
michael@0 | 2185 | } |
michael@0 | 2186 | } while ((parent = parent->GetParentDocument())); |
michael@0 | 2187 | |
michael@0 | 2188 | return true; |
michael@0 | 2189 | } |
michael@0 | 2190 | |
michael@0 | 2191 | void |
michael@0 | 2192 | nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) |
michael@0 | 2193 | { |
michael@0 | 2194 | nsCOMPtr<nsIURI> uri; |
michael@0 | 2195 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 2196 | if (aChannel) { |
michael@0 | 2197 | // Note: this code is duplicated in XULDocument::StartDocumentLoad and |
michael@0 | 2198 | // nsScriptSecurityManager::GetChannelPrincipal. |
michael@0 | 2199 | // Note: this should match nsDocShell::OnLoadingSite |
michael@0 | 2200 | NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); |
michael@0 | 2201 | |
michael@0 | 2202 | nsIScriptSecurityManager *securityManager = |
michael@0 | 2203 | nsContentUtils::GetSecurityManager(); |
michael@0 | 2204 | if (securityManager) { |
michael@0 | 2205 | securityManager->GetChannelPrincipal(aChannel, |
michael@0 | 2206 | getter_AddRefs(principal)); |
michael@0 | 2207 | } |
michael@0 | 2208 | } |
michael@0 | 2209 | |
michael@0 | 2210 | ResetToURI(uri, aLoadGroup, principal); |
michael@0 | 2211 | |
michael@0 | 2212 | nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel); |
michael@0 | 2213 | if (bag) { |
michael@0 | 2214 | nsCOMPtr<nsIURI> baseURI; |
michael@0 | 2215 | bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), |
michael@0 | 2216 | NS_GET_IID(nsIURI), getter_AddRefs(baseURI)); |
michael@0 | 2217 | if (baseURI) { |
michael@0 | 2218 | mDocumentBaseURI = baseURI; |
michael@0 | 2219 | mChromeXHRDocBaseURI = baseURI; |
michael@0 | 2220 | } |
michael@0 | 2221 | } |
michael@0 | 2222 | |
michael@0 | 2223 | mChannel = aChannel; |
michael@0 | 2224 | } |
michael@0 | 2225 | |
michael@0 | 2226 | void |
michael@0 | 2227 | nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, |
michael@0 | 2228 | nsIPrincipal* aPrincipal) |
michael@0 | 2229 | { |
michael@0 | 2230 | NS_PRECONDITION(aURI, "Null URI passed to ResetToURI"); |
michael@0 | 2231 | |
michael@0 | 2232 | #ifdef PR_LOGGING |
michael@0 | 2233 | if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 2234 | nsAutoCString spec; |
michael@0 | 2235 | aURI->GetSpec(spec); |
michael@0 | 2236 | PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get()); |
michael@0 | 2237 | } |
michael@0 | 2238 | #endif |
michael@0 | 2239 | |
michael@0 | 2240 | mSecurityInfo = nullptr; |
michael@0 | 2241 | |
michael@0 | 2242 | mDocumentLoadGroup = nullptr; |
michael@0 | 2243 | |
michael@0 | 2244 | // Delete references to sub-documents and kill the subdocument map, |
michael@0 | 2245 | // if any. It holds strong references |
michael@0 | 2246 | if (mSubDocuments) { |
michael@0 | 2247 | PL_DHashTableDestroy(mSubDocuments); |
michael@0 | 2248 | |
michael@0 | 2249 | mSubDocuments = nullptr; |
michael@0 | 2250 | } |
michael@0 | 2251 | |
michael@0 | 2252 | // Destroy link map now so we don't waste time removing |
michael@0 | 2253 | // links one by one |
michael@0 | 2254 | DestroyElementMaps(); |
michael@0 | 2255 | |
michael@0 | 2256 | bool oldVal = mInUnlinkOrDeletion; |
michael@0 | 2257 | mInUnlinkOrDeletion = true; |
michael@0 | 2258 | uint32_t count = mChildren.ChildCount(); |
michael@0 | 2259 | { // Scope for update |
michael@0 | 2260 | MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true); |
michael@0 | 2261 | for (int32_t i = int32_t(count) - 1; i >= 0; i--) { |
michael@0 | 2262 | nsCOMPtr<nsIContent> content = mChildren.ChildAt(i); |
michael@0 | 2263 | |
michael@0 | 2264 | nsIContent* previousSibling = content->GetPreviousSibling(); |
michael@0 | 2265 | |
michael@0 | 2266 | if (nsINode::GetFirstChild() == content) { |
michael@0 | 2267 | mFirstChild = content->GetNextSibling(); |
michael@0 | 2268 | } |
michael@0 | 2269 | mChildren.RemoveChildAt(i); |
michael@0 | 2270 | nsNodeUtils::ContentRemoved(this, content, i, previousSibling); |
michael@0 | 2271 | content->UnbindFromTree(); |
michael@0 | 2272 | } |
michael@0 | 2273 | mCachedRootElement = nullptr; |
michael@0 | 2274 | } |
michael@0 | 2275 | mInUnlinkOrDeletion = oldVal; |
michael@0 | 2276 | |
michael@0 | 2277 | mRegistry = nullptr; |
michael@0 | 2278 | |
michael@0 | 2279 | // Reset our stylesheets |
michael@0 | 2280 | ResetStylesheetsToURI(aURI); |
michael@0 | 2281 | |
michael@0 | 2282 | // Release the listener manager |
michael@0 | 2283 | if (mListenerManager) { |
michael@0 | 2284 | mListenerManager->Disconnect(); |
michael@0 | 2285 | mListenerManager = nullptr; |
michael@0 | 2286 | } |
michael@0 | 2287 | |
michael@0 | 2288 | // Release the stylesheets list. |
michael@0 | 2289 | mDOMStyleSheets = nullptr; |
michael@0 | 2290 | |
michael@0 | 2291 | // Release our principal after tearing down the document, rather than before. |
michael@0 | 2292 | // This ensures that, during teardown, the document and the dying window (which |
michael@0 | 2293 | // already nulled out its document pointer and cached the principal) have |
michael@0 | 2294 | // matching principals. |
michael@0 | 2295 | SetPrincipal(nullptr); |
michael@0 | 2296 | |
michael@0 | 2297 | // Clear the original URI so SetDocumentURI sets it. |
michael@0 | 2298 | mOriginalURI = nullptr; |
michael@0 | 2299 | |
michael@0 | 2300 | SetDocumentURI(aURI); |
michael@0 | 2301 | mChromeXHRDocURI = aURI; |
michael@0 | 2302 | // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns |
michael@0 | 2303 | // mDocumentURI. |
michael@0 | 2304 | mDocumentBaseURI = nullptr; |
michael@0 | 2305 | mChromeXHRDocBaseURI = nullptr; |
michael@0 | 2306 | |
michael@0 | 2307 | if (aLoadGroup) { |
michael@0 | 2308 | mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); |
michael@0 | 2309 | // there was an assertion here that aLoadGroup was not null. This |
michael@0 | 2310 | // is no longer valid: nsDocShell::SetDocument does not create a |
michael@0 | 2311 | // load group, and it works just fine |
michael@0 | 2312 | |
michael@0 | 2313 | // XXXbz what does "just fine" mean exactly? And given that there |
michael@0 | 2314 | // is no nsDocShell::SetDocument, what is this talking about? |
michael@0 | 2315 | } |
michael@0 | 2316 | |
michael@0 | 2317 | mLastModified.Truncate(); |
michael@0 | 2318 | // XXXbz I guess we're assuming that the caller will either pass in |
michael@0 | 2319 | // a channel with a useful type or call SetContentType? |
michael@0 | 2320 | SetContentTypeInternal(EmptyCString()); |
michael@0 | 2321 | mContentLanguage.Truncate(); |
michael@0 | 2322 | mBaseTarget.Truncate(); |
michael@0 | 2323 | mReferrer.Truncate(); |
michael@0 | 2324 | |
michael@0 | 2325 | mXMLDeclarationBits = 0; |
michael@0 | 2326 | |
michael@0 | 2327 | // Now get our new principal |
michael@0 | 2328 | if (aPrincipal) { |
michael@0 | 2329 | SetPrincipal(aPrincipal); |
michael@0 | 2330 | } else { |
michael@0 | 2331 | nsIScriptSecurityManager *securityManager = |
michael@0 | 2332 | nsContentUtils::GetSecurityManager(); |
michael@0 | 2333 | if (securityManager) { |
michael@0 | 2334 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 2335 | |
michael@0 | 2336 | if (!docShell && aLoadGroup) { |
michael@0 | 2337 | nsCOMPtr<nsIInterfaceRequestor> cbs; |
michael@0 | 2338 | aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs)); |
michael@0 | 2339 | docShell = do_GetInterface(cbs); |
michael@0 | 2340 | } |
michael@0 | 2341 | |
michael@0 | 2342 | MOZ_ASSERT(docShell, |
michael@0 | 2343 | "must be in a docshell or pass in an explicit principal"); |
michael@0 | 2344 | |
michael@0 | 2345 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 2346 | nsresult rv = securityManager-> |
michael@0 | 2347 | GetDocShellCodebasePrincipal(mDocumentURI, docShell, |
michael@0 | 2348 | getter_AddRefs(principal)); |
michael@0 | 2349 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 2350 | SetPrincipal(principal); |
michael@0 | 2351 | } |
michael@0 | 2352 | } |
michael@0 | 2353 | } |
michael@0 | 2354 | |
michael@0 | 2355 | // Refresh the principal on the compartment. |
michael@0 | 2356 | nsPIDOMWindow* win = GetInnerWindow(); |
michael@0 | 2357 | if (win) { |
michael@0 | 2358 | win->RefreshCompartmentPrincipal(); |
michael@0 | 2359 | } |
michael@0 | 2360 | } |
michael@0 | 2361 | |
michael@0 | 2362 | void |
michael@0 | 2363 | nsDocument::RemoveDocStyleSheetsFromStyleSets() |
michael@0 | 2364 | { |
michael@0 | 2365 | // The stylesheets should forget us |
michael@0 | 2366 | int32_t indx = mStyleSheets.Count(); |
michael@0 | 2367 | while (--indx >= 0) { |
michael@0 | 2368 | nsIStyleSheet* sheet = mStyleSheets[indx]; |
michael@0 | 2369 | sheet->SetOwningDocument(nullptr); |
michael@0 | 2370 | |
michael@0 | 2371 | if (sheet->IsApplicable()) { |
michael@0 | 2372 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 2373 | if (shell) { |
michael@0 | 2374 | shell->StyleSet()->RemoveDocStyleSheet(sheet); |
michael@0 | 2375 | } |
michael@0 | 2376 | } |
michael@0 | 2377 | // XXX Tell observers? |
michael@0 | 2378 | } |
michael@0 | 2379 | } |
michael@0 | 2380 | |
michael@0 | 2381 | void |
michael@0 | 2382 | nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType) |
michael@0 | 2383 | { |
michael@0 | 2384 | // The stylesheets should forget us |
michael@0 | 2385 | int32_t indx = aSheets.Count(); |
michael@0 | 2386 | while (--indx >= 0) { |
michael@0 | 2387 | nsIStyleSheet* sheet = aSheets[indx]; |
michael@0 | 2388 | sheet->SetOwningDocument(nullptr); |
michael@0 | 2389 | |
michael@0 | 2390 | if (sheet->IsApplicable()) { |
michael@0 | 2391 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 2392 | if (shell) { |
michael@0 | 2393 | shell->StyleSet()->RemoveStyleSheet(aType, sheet); |
michael@0 | 2394 | } |
michael@0 | 2395 | } |
michael@0 | 2396 | |
michael@0 | 2397 | // XXX Tell observers? |
michael@0 | 2398 | } |
michael@0 | 2399 | |
michael@0 | 2400 | } |
michael@0 | 2401 | |
michael@0 | 2402 | void |
michael@0 | 2403 | nsDocument::ResetStylesheetsToURI(nsIURI* aURI) |
michael@0 | 2404 | { |
michael@0 | 2405 | MOZ_ASSERT(aURI); |
michael@0 | 2406 | |
michael@0 | 2407 | mozAutoDocUpdate upd(this, UPDATE_STYLE, true); |
michael@0 | 2408 | RemoveDocStyleSheetsFromStyleSets(); |
michael@0 | 2409 | RemoveStyleSheetsFromStyleSets(mCatalogSheets, nsStyleSet::eAgentSheet); |
michael@0 | 2410 | RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet); |
michael@0 | 2411 | RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet); |
michael@0 | 2412 | RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet); |
michael@0 | 2413 | |
michael@0 | 2414 | // Release all the sheets |
michael@0 | 2415 | mStyleSheets.Clear(); |
michael@0 | 2416 | for (uint32_t i = 0; i < SheetTypeCount; ++i) |
michael@0 | 2417 | mAdditionalSheets[i].Clear(); |
michael@0 | 2418 | |
michael@0 | 2419 | // NOTE: We don't release the catalog sheets. It doesn't really matter |
michael@0 | 2420 | // now, but it could in the future -- in which case not releasing them |
michael@0 | 2421 | // is probably the right thing to do. |
michael@0 | 2422 | |
michael@0 | 2423 | // Now reset our inline style and attribute sheets. |
michael@0 | 2424 | if (mAttrStyleSheet) { |
michael@0 | 2425 | mAttrStyleSheet->Reset(); |
michael@0 | 2426 | mAttrStyleSheet->SetOwningDocument(this); |
michael@0 | 2427 | } else { |
michael@0 | 2428 | mAttrStyleSheet = new nsHTMLStyleSheet(this); |
michael@0 | 2429 | } |
michael@0 | 2430 | |
michael@0 | 2431 | if (!mStyleAttrStyleSheet) { |
michael@0 | 2432 | mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet(); |
michael@0 | 2433 | } |
michael@0 | 2434 | |
michael@0 | 2435 | // Now set up our style sets |
michael@0 | 2436 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 2437 | if (shell) { |
michael@0 | 2438 | FillStyleSet(shell->StyleSet()); |
michael@0 | 2439 | } |
michael@0 | 2440 | } |
michael@0 | 2441 | |
michael@0 | 2442 | static bool |
michael@0 | 2443 | AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData) |
michael@0 | 2444 | { |
michael@0 | 2445 | nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData); |
michael@0 | 2446 | styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet); |
michael@0 | 2447 | return true; |
michael@0 | 2448 | } |
michael@0 | 2449 | |
michael@0 | 2450 | static void |
michael@0 | 2451 | AppendSheetsToStyleSet(nsStyleSet* aStyleSet, |
michael@0 | 2452 | const nsCOMArray<nsIStyleSheet>& aSheets, |
michael@0 | 2453 | nsStyleSet::sheetType aType) |
michael@0 | 2454 | { |
michael@0 | 2455 | for (int32_t i = aSheets.Count() - 1; i >= 0; --i) { |
michael@0 | 2456 | aStyleSet->AppendStyleSheet(aType, aSheets[i]); |
michael@0 | 2457 | } |
michael@0 | 2458 | } |
michael@0 | 2459 | |
michael@0 | 2460 | |
michael@0 | 2461 | void |
michael@0 | 2462 | nsDocument::FillStyleSet(nsStyleSet* aStyleSet) |
michael@0 | 2463 | { |
michael@0 | 2464 | NS_PRECONDITION(aStyleSet, "Must have a style set"); |
michael@0 | 2465 | NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0, |
michael@0 | 2466 | "Style set already has document sheets?"); |
michael@0 | 2467 | |
michael@0 | 2468 | // We could consider moving this to nsStyleSet::Init, to match its |
michael@0 | 2469 | // handling of the eAnimationSheet and eTransitionSheet levels. |
michael@0 | 2470 | aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet); |
michael@0 | 2471 | aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet); |
michael@0 | 2472 | |
michael@0 | 2473 | int32_t i; |
michael@0 | 2474 | for (i = mStyleSheets.Count() - 1; i >= 0; --i) { |
michael@0 | 2475 | nsIStyleSheet* sheet = mStyleSheets[i]; |
michael@0 | 2476 | if (sheet->IsApplicable()) { |
michael@0 | 2477 | aStyleSet->AddDocStyleSheet(sheet, this); |
michael@0 | 2478 | } |
michael@0 | 2479 | } |
michael@0 | 2480 | |
michael@0 | 2481 | nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); |
michael@0 | 2482 | if (sheetService) { |
michael@0 | 2483 | sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet, |
michael@0 | 2484 | aStyleSet); |
michael@0 | 2485 | } |
michael@0 | 2486 | |
michael@0 | 2487 | for (i = mCatalogSheets.Count() - 1; i >= 0; --i) { |
michael@0 | 2488 | nsIStyleSheet* sheet = mCatalogSheets[i]; |
michael@0 | 2489 | if (sheet->IsApplicable()) { |
michael@0 | 2490 | aStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet); |
michael@0 | 2491 | } |
michael@0 | 2492 | } |
michael@0 | 2493 | |
michael@0 | 2494 | AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet], |
michael@0 | 2495 | nsStyleSet::eAgentSheet); |
michael@0 | 2496 | AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet], |
michael@0 | 2497 | nsStyleSet::eUserSheet); |
michael@0 | 2498 | AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet], |
michael@0 | 2499 | nsStyleSet::eDocSheet); |
michael@0 | 2500 | } |
michael@0 | 2501 | |
michael@0 | 2502 | nsresult |
michael@0 | 2503 | nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, |
michael@0 | 2504 | nsILoadGroup* aLoadGroup, |
michael@0 | 2505 | nsISupports* aContainer, |
michael@0 | 2506 | nsIStreamListener **aDocListener, |
michael@0 | 2507 | bool aReset, nsIContentSink* aSink) |
michael@0 | 2508 | { |
michael@0 | 2509 | #ifdef PR_LOGGING |
michael@0 | 2510 | if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 2511 | nsCOMPtr<nsIURI> uri; |
michael@0 | 2512 | aChannel->GetURI(getter_AddRefs(uri)); |
michael@0 | 2513 | nsAutoCString spec; |
michael@0 | 2514 | if (uri) |
michael@0 | 2515 | uri->GetSpec(spec); |
michael@0 | 2516 | PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get()); |
michael@0 | 2517 | } |
michael@0 | 2518 | #endif |
michael@0 | 2519 | |
michael@0 | 2520 | #ifdef DEBUG |
michael@0 | 2521 | { |
michael@0 | 2522 | uint32_t appId; |
michael@0 | 2523 | nsresult rv = NodePrincipal()->GetAppId(&appId); |
michael@0 | 2524 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2525 | MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID, |
michael@0 | 2526 | "Document should never have UNKNOWN_APP_ID"); |
michael@0 | 2527 | } |
michael@0 | 2528 | #endif |
michael@0 | 2529 | |
michael@0 | 2530 | MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED, |
michael@0 | 2531 | "Bad readyState"); |
michael@0 | 2532 | SetReadyStateInternal(READYSTATE_LOADING); |
michael@0 | 2533 | |
michael@0 | 2534 | if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) { |
michael@0 | 2535 | mLoadedAsData = true; |
michael@0 | 2536 | // We need to disable script & style loading in this case. |
michael@0 | 2537 | // We leave them disabled even in EndLoad(), and let anyone |
michael@0 | 2538 | // who puts the document on display to worry about enabling. |
michael@0 | 2539 | |
michael@0 | 2540 | // Do not load/process scripts when loading as data |
michael@0 | 2541 | ScriptLoader()->SetEnabled(false); |
michael@0 | 2542 | |
michael@0 | 2543 | // styles |
michael@0 | 2544 | CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data |
michael@0 | 2545 | } else if (nsCRT::strcmp("external-resource", aCommand) == 0) { |
michael@0 | 2546 | // Allow CSS, but not scripts |
michael@0 | 2547 | ScriptLoader()->SetEnabled(false); |
michael@0 | 2548 | } |
michael@0 | 2549 | |
michael@0 | 2550 | mMayStartLayout = false; |
michael@0 | 2551 | |
michael@0 | 2552 | mHaveInputEncoding = true; |
michael@0 | 2553 | |
michael@0 | 2554 | if (aReset) { |
michael@0 | 2555 | Reset(aChannel, aLoadGroup); |
michael@0 | 2556 | } |
michael@0 | 2557 | |
michael@0 | 2558 | nsAutoCString contentType; |
michael@0 | 2559 | nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel); |
michael@0 | 2560 | if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString( |
michael@0 | 2561 | NS_LITERAL_STRING("contentType"), contentType))) || |
michael@0 | 2562 | NS_SUCCEEDED(aChannel->GetContentType(contentType))) { |
michael@0 | 2563 | // XXX this is only necessary for viewsource: |
michael@0 | 2564 | nsACString::const_iterator start, end, semicolon; |
michael@0 | 2565 | contentType.BeginReading(start); |
michael@0 | 2566 | contentType.EndReading(end); |
michael@0 | 2567 | semicolon = start; |
michael@0 | 2568 | FindCharInReadable(';', semicolon, end); |
michael@0 | 2569 | SetContentTypeInternal(Substring(start, semicolon)); |
michael@0 | 2570 | } |
michael@0 | 2571 | |
michael@0 | 2572 | RetrieveRelevantHeaders(aChannel); |
michael@0 | 2573 | |
michael@0 | 2574 | mChannel = aChannel; |
michael@0 | 2575 | nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel); |
michael@0 | 2576 | if (inStrmChan) { |
michael@0 | 2577 | bool isSrcdocChannel; |
michael@0 | 2578 | inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel); |
michael@0 | 2579 | if (isSrcdocChannel) { |
michael@0 | 2580 | mIsSrcdocDocument = true; |
michael@0 | 2581 | } |
michael@0 | 2582 | } |
michael@0 | 2583 | |
michael@0 | 2584 | // If this document is being loaded by a docshell, copy its sandbox flags |
michael@0 | 2585 | // to the document. These are immutable after being set here. |
michael@0 | 2586 | nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer); |
michael@0 | 2587 | |
michael@0 | 2588 | if (docShell) { |
michael@0 | 2589 | nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags); |
michael@0 | 2590 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2591 | } |
michael@0 | 2592 | |
michael@0 | 2593 | // If this is not a data document, set CSP. |
michael@0 | 2594 | if (!mLoadedAsData) { |
michael@0 | 2595 | nsresult rv = InitCSP(aChannel); |
michael@0 | 2596 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2597 | } |
michael@0 | 2598 | |
michael@0 | 2599 | return NS_OK; |
michael@0 | 2600 | } |
michael@0 | 2601 | |
michael@0 | 2602 | void |
michael@0 | 2603 | CSPErrorQueue::Add(const char* aMessageName) |
michael@0 | 2604 | { |
michael@0 | 2605 | mErrors.AppendElement(aMessageName); |
michael@0 | 2606 | } |
michael@0 | 2607 | |
michael@0 | 2608 | void |
michael@0 | 2609 | CSPErrorQueue::Flush(nsIDocument* aDocument) |
michael@0 | 2610 | { |
michael@0 | 2611 | for (uint32_t i = 0; i < mErrors.Length(); i++) { |
michael@0 | 2612 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 2613 | NS_LITERAL_CSTRING("CSP"), aDocument, |
michael@0 | 2614 | nsContentUtils::eSECURITY_PROPERTIES, |
michael@0 | 2615 | mErrors[i]); |
michael@0 | 2616 | } |
michael@0 | 2617 | mErrors.Clear(); |
michael@0 | 2618 | } |
michael@0 | 2619 | |
michael@0 | 2620 | void |
michael@0 | 2621 | nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) |
michael@0 | 2622 | { |
michael@0 | 2623 | for (uint32_t i = 0; i < aMessages.Length(); ++i) { |
michael@0 | 2624 | nsAutoString messageTag; |
michael@0 | 2625 | aMessages[i]->GetTag(messageTag); |
michael@0 | 2626 | |
michael@0 | 2627 | nsAutoString category; |
michael@0 | 2628 | aMessages[i]->GetCategory(category); |
michael@0 | 2629 | |
michael@0 | 2630 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 2631 | NS_ConvertUTF16toUTF8(category), |
michael@0 | 2632 | this, nsContentUtils::eSECURITY_PROPERTIES, |
michael@0 | 2633 | NS_ConvertUTF16toUTF8(messageTag).get()); |
michael@0 | 2634 | } |
michael@0 | 2635 | } |
michael@0 | 2636 | |
michael@0 | 2637 | static nsresult |
michael@0 | 2638 | AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue, |
michael@0 | 2639 | nsIURI* aSelfURI, bool aReportOnly, bool aSpecCompliant) |
michael@0 | 2640 | { |
michael@0 | 2641 | // Need to tokenize the header value since multiple headers could be |
michael@0 | 2642 | // concatenated into one comma-separated list of policies. |
michael@0 | 2643 | // See RFC2616 section 4.2 (last paragraph) |
michael@0 | 2644 | nsresult rv = NS_OK; |
michael@0 | 2645 | nsCharSeparatedTokenizer tokenizer(aHeaderValue, ','); |
michael@0 | 2646 | while (tokenizer.hasMoreTokens()) { |
michael@0 | 2647 | const nsSubstring& policy = tokenizer.nextToken(); |
michael@0 | 2648 | rv = csp->AppendPolicy(policy, aSelfURI, aReportOnly, aSpecCompliant); |
michael@0 | 2649 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2650 | #ifdef PR_LOGGING |
michael@0 | 2651 | { |
michael@0 | 2652 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
michael@0 | 2653 | ("CSP refined with policy: \"%s\"", |
michael@0 | 2654 | NS_ConvertUTF16toUTF8(policy).get())); |
michael@0 | 2655 | } |
michael@0 | 2656 | #endif |
michael@0 | 2657 | } |
michael@0 | 2658 | return NS_OK; |
michael@0 | 2659 | } |
michael@0 | 2660 | |
michael@0 | 2661 | nsresult |
michael@0 | 2662 | nsDocument::InitCSP(nsIChannel* aChannel) |
michael@0 | 2663 | { |
michael@0 | 2664 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
michael@0 | 2665 | if (!CSPService::sCSPEnabled) { |
michael@0 | 2666 | #ifdef PR_LOGGING |
michael@0 | 2667 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
michael@0 | 2668 | ("CSP is disabled, skipping CSP init for document %p", this)); |
michael@0 | 2669 | #endif |
michael@0 | 2670 | return NS_OK; |
michael@0 | 2671 | } |
michael@0 | 2672 | |
michael@0 | 2673 | nsAutoCString tCspHeaderValue, tCspROHeaderValue; |
michael@0 | 2674 | nsAutoCString tCspOldHeaderValue, tCspOldROHeaderValue; |
michael@0 | 2675 | |
michael@0 | 2676 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); |
michael@0 | 2677 | if (httpChannel) { |
michael@0 | 2678 | httpChannel->GetResponseHeader( |
michael@0 | 2679 | NS_LITERAL_CSTRING("x-content-security-policy"), |
michael@0 | 2680 | tCspOldHeaderValue); |
michael@0 | 2681 | |
michael@0 | 2682 | httpChannel->GetResponseHeader( |
michael@0 | 2683 | NS_LITERAL_CSTRING("x-content-security-policy-report-only"), |
michael@0 | 2684 | tCspOldROHeaderValue); |
michael@0 | 2685 | |
michael@0 | 2686 | httpChannel->GetResponseHeader( |
michael@0 | 2687 | NS_LITERAL_CSTRING("content-security-policy"), |
michael@0 | 2688 | tCspHeaderValue); |
michael@0 | 2689 | |
michael@0 | 2690 | httpChannel->GetResponseHeader( |
michael@0 | 2691 | NS_LITERAL_CSTRING("content-security-policy-report-only"), |
michael@0 | 2692 | tCspROHeaderValue); |
michael@0 | 2693 | } |
michael@0 | 2694 | NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue); |
michael@0 | 2695 | NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue); |
michael@0 | 2696 | NS_ConvertASCIItoUTF16 cspOldHeaderValue(tCspOldHeaderValue); |
michael@0 | 2697 | NS_ConvertASCIItoUTF16 cspOldROHeaderValue(tCspOldROHeaderValue); |
michael@0 | 2698 | |
michael@0 | 2699 | // Only use the CSP 1.0 spec compliant headers if a pref to do so |
michael@0 | 2700 | // is set. This lets us turn on the 1.0 parser per platform. This |
michael@0 | 2701 | // pref is also set by the tests for 1.0 spec compliant CSP. |
michael@0 | 2702 | bool specCompliantEnabled = |
michael@0 | 2703 | Preferences::GetBool("security.csp.speccompliant"); |
michael@0 | 2704 | |
michael@0 | 2705 | // If spec compliant pref isn't set, pretend we never got these headers. |
michael@0 | 2706 | if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) && |
michael@0 | 2707 | !specCompliantEnabled) { |
michael@0 | 2708 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
michael@0 | 2709 | ("Got spec compliant CSP headers but pref was not set")); |
michael@0 | 2710 | cspHeaderValue.Truncate(); |
michael@0 | 2711 | cspROHeaderValue.Truncate(); |
michael@0 | 2712 | } |
michael@0 | 2713 | |
michael@0 | 2714 | // If both the new header AND the old header are present, warn that |
michael@0 | 2715 | // the old header will be ignored. Otherwise, if the old header is |
michael@0 | 2716 | // present, warn that it will be deprecated. |
michael@0 | 2717 | bool oldHeaderIsPresent = !cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty(); |
michael@0 | 2718 | bool newHeaderIsPresent = !cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty(); |
michael@0 | 2719 | |
michael@0 | 2720 | if (oldHeaderIsPresent && newHeaderIsPresent) { |
michael@0 | 2721 | mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent"); |
michael@0 | 2722 | } else if (oldHeaderIsPresent) { |
michael@0 | 2723 | mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated"); |
michael@0 | 2724 | } |
michael@0 | 2725 | |
michael@0 | 2726 | // Figure out if we need to apply an app default CSP or a CSP from an app manifest |
michael@0 | 2727 | nsIPrincipal* principal = NodePrincipal(); |
michael@0 | 2728 | |
michael@0 | 2729 | uint16_t appStatus = principal->GetAppStatus(); |
michael@0 | 2730 | bool applyAppDefaultCSP = appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED || |
michael@0 | 2731 | appStatus == nsIPrincipal::APP_STATUS_CERTIFIED; |
michael@0 | 2732 | bool applyAppManifestCSP = false; |
michael@0 | 2733 | |
michael@0 | 2734 | nsAutoString appManifestCSP; |
michael@0 | 2735 | if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) { |
michael@0 | 2736 | nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); |
michael@0 | 2737 | if (appsService) { |
michael@0 | 2738 | uint32_t appId = 0; |
michael@0 | 2739 | if (NS_SUCCEEDED(principal->GetAppId(&appId))) { |
michael@0 | 2740 | appsService->GetCSPByLocalId(appId, appManifestCSP); |
michael@0 | 2741 | if (!appManifestCSP.IsEmpty()) { |
michael@0 | 2742 | applyAppManifestCSP = true; |
michael@0 | 2743 | } |
michael@0 | 2744 | } |
michael@0 | 2745 | } |
michael@0 | 2746 | } |
michael@0 | 2747 | |
michael@0 | 2748 | // If there's no CSP to apply, go ahead and return early |
michael@0 | 2749 | if (!applyAppDefaultCSP && |
michael@0 | 2750 | !applyAppManifestCSP && |
michael@0 | 2751 | cspHeaderValue.IsEmpty() && |
michael@0 | 2752 | cspROHeaderValue.IsEmpty() && |
michael@0 | 2753 | cspOldHeaderValue.IsEmpty() && |
michael@0 | 2754 | cspOldROHeaderValue.IsEmpty()) { |
michael@0 | 2755 | #ifdef PR_LOGGING |
michael@0 | 2756 | nsCOMPtr<nsIURI> chanURI; |
michael@0 | 2757 | aChannel->GetURI(getter_AddRefs(chanURI)); |
michael@0 | 2758 | nsAutoCString aspec; |
michael@0 | 2759 | chanURI->GetAsciiSpec(aspec); |
michael@0 | 2760 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
michael@0 | 2761 | ("no CSP for document, %s, %s", |
michael@0 | 2762 | aspec.get(), |
michael@0 | 2763 | applyAppDefaultCSP ? "is app" : "not an app")); |
michael@0 | 2764 | #endif |
michael@0 | 2765 | return NS_OK; |
michael@0 | 2766 | } |
michael@0 | 2767 | |
michael@0 | 2768 | #ifdef PR_LOGGING |
michael@0 | 2769 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this)); |
michael@0 | 2770 | #endif |
michael@0 | 2771 | |
michael@0 | 2772 | nsresult rv; |
michael@0 | 2773 | |
michael@0 | 2774 | // If Document is an app check to see if we already set CSP and return early |
michael@0 | 2775 | // if that is indeed the case. |
michael@0 | 2776 | // |
michael@0 | 2777 | // In general (see bug 947831), we should not be setting CSP on a principal |
michael@0 | 2778 | // that aliases another document. For non-app code this is not a problem |
michael@0 | 2779 | // since we only share the underlying principal with nested browsing |
michael@0 | 2780 | // contexts for which a header cannot be set (e.g., about:blank and |
michael@0 | 2781 | // about:srcodoc iframes) and thus won't try to set the CSP again. This |
michael@0 | 2782 | // check ensures that we do not try to set CSP for an app. |
michael@0 | 2783 | if (applyAppDefaultCSP || applyAppManifestCSP) { |
michael@0 | 2784 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
michael@0 | 2785 | rv = principal->GetCsp(getter_AddRefs(csp)); |
michael@0 | 2786 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2787 | |
michael@0 | 2788 | if (csp) { |
michael@0 | 2789 | #ifdef PR_LOGGING |
michael@0 | 2790 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s", |
michael@0 | 2791 | "This document is sharing principal with another document.", |
michael@0 | 2792 | "Since the document is an app, CSP was already set.", |
michael@0 | 2793 | "Skipping attempt to set CSP.")); |
michael@0 | 2794 | #endif |
michael@0 | 2795 | return NS_OK; |
michael@0 | 2796 | } |
michael@0 | 2797 | } |
michael@0 | 2798 | |
michael@0 | 2799 | // create new CSP object |
michael@0 | 2800 | csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv); |
michael@0 | 2801 | |
michael@0 | 2802 | if (NS_FAILED(rv)) { |
michael@0 | 2803 | #ifdef PR_LOGGING |
michael@0 | 2804 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv)); |
michael@0 | 2805 | #endif |
michael@0 | 2806 | return rv; |
michael@0 | 2807 | } |
michael@0 | 2808 | |
michael@0 | 2809 | // used as a "self" identifier for the CSP. |
michael@0 | 2810 | nsCOMPtr<nsIURI> selfURI; |
michael@0 | 2811 | aChannel->GetURI(getter_AddRefs(selfURI)); |
michael@0 | 2812 | |
michael@0 | 2813 | // Store the request context for violation reports |
michael@0 | 2814 | csp->SetRequestContext(nullptr, nullptr, nullptr, aChannel); |
michael@0 | 2815 | |
michael@0 | 2816 | // ----- if the doc is an app and we want a default CSP, apply it. |
michael@0 | 2817 | if (applyAppDefaultCSP) { |
michael@0 | 2818 | nsAdoptingString appCSP; |
michael@0 | 2819 | if (appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) { |
michael@0 | 2820 | appCSP = Preferences::GetString("security.apps.privileged.CSP.default"); |
michael@0 | 2821 | NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default"); |
michael@0 | 2822 | } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) { |
michael@0 | 2823 | appCSP = Preferences::GetString("security.apps.certified.CSP.default"); |
michael@0 | 2824 | NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default"); |
michael@0 | 2825 | } |
michael@0 | 2826 | |
michael@0 | 2827 | if (appCSP) { |
michael@0 | 2828 | // Use the 1.0 CSP parser for apps if the pref to do so is set. |
michael@0 | 2829 | csp->AppendPolicy(appCSP, selfURI, false, specCompliantEnabled); |
michael@0 | 2830 | } |
michael@0 | 2831 | } |
michael@0 | 2832 | |
michael@0 | 2833 | // ----- if the doc is an app and specifies a CSP in its manifest, apply it. |
michael@0 | 2834 | if (applyAppManifestCSP) { |
michael@0 | 2835 | // Use the 1.0 CSP parser for apps if the pref to do so is set. |
michael@0 | 2836 | csp->AppendPolicy(appManifestCSP, selfURI, false, specCompliantEnabled); |
michael@0 | 2837 | } |
michael@0 | 2838 | |
michael@0 | 2839 | // While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers |
michael@0 | 2840 | // take priority. If both are present, the x-* headers are ignored. |
michael@0 | 2841 | |
michael@0 | 2842 | // ----- if there's a full-strength CSP header, apply it. |
michael@0 | 2843 | if (!cspHeaderValue.IsEmpty()) { |
michael@0 | 2844 | rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true); |
michael@0 | 2845 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2846 | } else if (!cspOldHeaderValue.IsEmpty()) { |
michael@0 | 2847 | rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false); |
michael@0 | 2848 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2849 | } |
michael@0 | 2850 | |
michael@0 | 2851 | // ----- if there's a report-only CSP header, apply it. |
michael@0 | 2852 | if (!cspROHeaderValue.IsEmpty()) { |
michael@0 | 2853 | rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true); |
michael@0 | 2854 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2855 | } else if (!cspOldROHeaderValue.IsEmpty()) { |
michael@0 | 2856 | rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false); |
michael@0 | 2857 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2858 | } |
michael@0 | 2859 | |
michael@0 | 2860 | // ----- Enforce frame-ancestor policy on any applied policies |
michael@0 | 2861 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 2862 | if (docShell) { |
michael@0 | 2863 | bool safeAncestry = false; |
michael@0 | 2864 | |
michael@0 | 2865 | // PermitsAncestry sends violation reports when necessary |
michael@0 | 2866 | rv = csp->PermitsAncestry(docShell, &safeAncestry); |
michael@0 | 2867 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2868 | |
michael@0 | 2869 | if (!safeAncestry) { |
michael@0 | 2870 | #ifdef PR_LOGGING |
michael@0 | 2871 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
michael@0 | 2872 | ("CSP doesn't like frame's ancestry, not loading.")); |
michael@0 | 2873 | #endif |
michael@0 | 2874 | // stop! ERROR page! |
michael@0 | 2875 | aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); |
michael@0 | 2876 | } |
michael@0 | 2877 | } |
michael@0 | 2878 | |
michael@0 | 2879 | rv = principal->SetCsp(csp); |
michael@0 | 2880 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2881 | #ifdef PR_LOGGING |
michael@0 | 2882 | PR_LOG(gCspPRLog, PR_LOG_DEBUG, |
michael@0 | 2883 | ("Inserted CSP into principal %p", principal)); |
michael@0 | 2884 | #endif |
michael@0 | 2885 | |
michael@0 | 2886 | return NS_OK; |
michael@0 | 2887 | } |
michael@0 | 2888 | |
michael@0 | 2889 | void |
michael@0 | 2890 | nsDocument::StopDocumentLoad() |
michael@0 | 2891 | { |
michael@0 | 2892 | if (mParser) { |
michael@0 | 2893 | mParserAborted = true; |
michael@0 | 2894 | mParser->Terminate(); |
michael@0 | 2895 | } |
michael@0 | 2896 | } |
michael@0 | 2897 | |
michael@0 | 2898 | void |
michael@0 | 2899 | nsDocument::SetDocumentURI(nsIURI* aURI) |
michael@0 | 2900 | { |
michael@0 | 2901 | nsCOMPtr<nsIURI> oldBase = GetDocBaseURI(); |
michael@0 | 2902 | mDocumentURI = NS_TryToMakeImmutable(aURI); |
michael@0 | 2903 | nsIURI* newBase = GetDocBaseURI(); |
michael@0 | 2904 | |
michael@0 | 2905 | bool equalBases = false; |
michael@0 | 2906 | // Changing just the ref of a URI does not change how relative URIs would |
michael@0 | 2907 | // resolve wrt to it, so we can treat the bases as equal as long as they're |
michael@0 | 2908 | // equal ignoring the ref. |
michael@0 | 2909 | if (oldBase && newBase) { |
michael@0 | 2910 | oldBase->EqualsExceptRef(newBase, &equalBases); |
michael@0 | 2911 | } |
michael@0 | 2912 | else { |
michael@0 | 2913 | equalBases = !oldBase && !newBase; |
michael@0 | 2914 | } |
michael@0 | 2915 | |
michael@0 | 2916 | // If this is the first time we're setting the document's URI, set the |
michael@0 | 2917 | // document's original URI. |
michael@0 | 2918 | if (!mOriginalURI) |
michael@0 | 2919 | mOriginalURI = mDocumentURI; |
michael@0 | 2920 | |
michael@0 | 2921 | // If changing the document's URI changed the base URI of the document, we |
michael@0 | 2922 | // need to refresh the hrefs of all the links on the page. |
michael@0 | 2923 | if (!equalBases) { |
michael@0 | 2924 | RefreshLinkHrefs(); |
michael@0 | 2925 | } |
michael@0 | 2926 | } |
michael@0 | 2927 | |
michael@0 | 2928 | void |
michael@0 | 2929 | nsDocument::SetChromeXHRDocURI(nsIURI* aURI) |
michael@0 | 2930 | { |
michael@0 | 2931 | mChromeXHRDocURI = aURI; |
michael@0 | 2932 | } |
michael@0 | 2933 | |
michael@0 | 2934 | void |
michael@0 | 2935 | nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI) |
michael@0 | 2936 | { |
michael@0 | 2937 | mChromeXHRDocBaseURI = aURI; |
michael@0 | 2938 | } |
michael@0 | 2939 | |
michael@0 | 2940 | NS_IMETHODIMP |
michael@0 | 2941 | nsDocument::GetLastModified(nsAString& aLastModified) |
michael@0 | 2942 | { |
michael@0 | 2943 | nsIDocument::GetLastModified(aLastModified); |
michael@0 | 2944 | return NS_OK; |
michael@0 | 2945 | } |
michael@0 | 2946 | |
michael@0 | 2947 | void |
michael@0 | 2948 | nsIDocument::GetLastModified(nsAString& aLastModified) const |
michael@0 | 2949 | { |
michael@0 | 2950 | if (!mLastModified.IsEmpty()) { |
michael@0 | 2951 | aLastModified.Assign(mLastModified); |
michael@0 | 2952 | } else { |
michael@0 | 2953 | // If we for whatever reason failed to find the last modified time |
michael@0 | 2954 | // (or even the current time), fall back to what NS4.x returned. |
michael@0 | 2955 | aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00")); |
michael@0 | 2956 | } |
michael@0 | 2957 | } |
michael@0 | 2958 | |
michael@0 | 2959 | void |
michael@0 | 2960 | nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName) |
michael@0 | 2961 | { |
michael@0 | 2962 | MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement), |
michael@0 | 2963 | "Only put elements that need to be exposed as document['name'] in " |
michael@0 | 2964 | "the named table."); |
michael@0 | 2965 | |
michael@0 | 2966 | nsIdentifierMapEntry *entry = |
michael@0 | 2967 | mIdentifierMap.PutEntry(nsDependentAtomString(aName)); |
michael@0 | 2968 | |
michael@0 | 2969 | // Null for out-of-memory |
michael@0 | 2970 | if (entry) { |
michael@0 | 2971 | if (!entry->HasNameElement() && |
michael@0 | 2972 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
michael@0 | 2973 | ++mExpandoAndGeneration.generation; |
michael@0 | 2974 | } |
michael@0 | 2975 | entry->AddNameElement(this, aElement); |
michael@0 | 2976 | } |
michael@0 | 2977 | } |
michael@0 | 2978 | |
michael@0 | 2979 | void |
michael@0 | 2980 | nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName) |
michael@0 | 2981 | { |
michael@0 | 2982 | // Speed up document teardown |
michael@0 | 2983 | if (mIdentifierMap.Count() == 0) |
michael@0 | 2984 | return; |
michael@0 | 2985 | |
michael@0 | 2986 | nsIdentifierMapEntry *entry = |
michael@0 | 2987 | mIdentifierMap.GetEntry(nsDependentAtomString(aName)); |
michael@0 | 2988 | if (!entry) // Could be false if the element was anonymous, hence never added |
michael@0 | 2989 | return; |
michael@0 | 2990 | |
michael@0 | 2991 | entry->RemoveNameElement(aElement); |
michael@0 | 2992 | if (!entry->HasNameElement() && |
michael@0 | 2993 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
michael@0 | 2994 | ++mExpandoAndGeneration.generation; |
michael@0 | 2995 | } |
michael@0 | 2996 | } |
michael@0 | 2997 | |
michael@0 | 2998 | void |
michael@0 | 2999 | nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId) |
michael@0 | 3000 | { |
michael@0 | 3001 | nsIdentifierMapEntry *entry = |
michael@0 | 3002 | mIdentifierMap.PutEntry(nsDependentAtomString(aId)); |
michael@0 | 3003 | |
michael@0 | 3004 | if (entry) { /* True except on OOM */ |
michael@0 | 3005 | if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && |
michael@0 | 3006 | !entry->HasNameElement() && |
michael@0 | 3007 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
michael@0 | 3008 | ++mExpandoAndGeneration.generation; |
michael@0 | 3009 | } |
michael@0 | 3010 | entry->AddIdElement(aElement); |
michael@0 | 3011 | } |
michael@0 | 3012 | } |
michael@0 | 3013 | |
michael@0 | 3014 | void |
michael@0 | 3015 | nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId) |
michael@0 | 3016 | { |
michael@0 | 3017 | NS_ASSERTION(aId, "huhwhatnow?"); |
michael@0 | 3018 | |
michael@0 | 3019 | // Speed up document teardown |
michael@0 | 3020 | if (mIdentifierMap.Count() == 0) { |
michael@0 | 3021 | return; |
michael@0 | 3022 | } |
michael@0 | 3023 | |
michael@0 | 3024 | nsIdentifierMapEntry *entry = |
michael@0 | 3025 | mIdentifierMap.GetEntry(nsDependentAtomString(aId)); |
michael@0 | 3026 | if (!entry) // Can be null for XML elements with changing ids. |
michael@0 | 3027 | return; |
michael@0 | 3028 | |
michael@0 | 3029 | entry->RemoveIdElement(aElement); |
michael@0 | 3030 | if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && |
michael@0 | 3031 | !entry->HasNameElement() && |
michael@0 | 3032 | !entry->HasIdElementExposedAsHTMLDocumentProperty()) { |
michael@0 | 3033 | ++mExpandoAndGeneration.generation; |
michael@0 | 3034 | } |
michael@0 | 3035 | if (entry->IsEmpty()) { |
michael@0 | 3036 | mIdentifierMap.RawRemoveEntry(entry); |
michael@0 | 3037 | } |
michael@0 | 3038 | } |
michael@0 | 3039 | |
michael@0 | 3040 | nsIPrincipal* |
michael@0 | 3041 | nsDocument::GetPrincipal() |
michael@0 | 3042 | { |
michael@0 | 3043 | return NodePrincipal(); |
michael@0 | 3044 | } |
michael@0 | 3045 | |
michael@0 | 3046 | extern bool sDisablePrefetchHTTPSPref; |
michael@0 | 3047 | |
michael@0 | 3048 | void |
michael@0 | 3049 | nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal) |
michael@0 | 3050 | { |
michael@0 | 3051 | if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) { |
michael@0 | 3052 | nsCOMPtr<nsIURI> uri; |
michael@0 | 3053 | aNewPrincipal->GetURI(getter_AddRefs(uri)); |
michael@0 | 3054 | bool isHTTPS; |
michael@0 | 3055 | if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) || |
michael@0 | 3056 | isHTTPS) { |
michael@0 | 3057 | mAllowDNSPrefetch = false; |
michael@0 | 3058 | } |
michael@0 | 3059 | } |
michael@0 | 3060 | mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal); |
michael@0 | 3061 | } |
michael@0 | 3062 | |
michael@0 | 3063 | NS_IMETHODIMP |
michael@0 | 3064 | nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache) |
michael@0 | 3065 | { |
michael@0 | 3066 | NS_IF_ADDREF(*aApplicationCache = mApplicationCache); |
michael@0 | 3067 | |
michael@0 | 3068 | return NS_OK; |
michael@0 | 3069 | } |
michael@0 | 3070 | |
michael@0 | 3071 | NS_IMETHODIMP |
michael@0 | 3072 | nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache) |
michael@0 | 3073 | { |
michael@0 | 3074 | mApplicationCache = aApplicationCache; |
michael@0 | 3075 | |
michael@0 | 3076 | return NS_OK; |
michael@0 | 3077 | } |
michael@0 | 3078 | |
michael@0 | 3079 | NS_IMETHODIMP |
michael@0 | 3080 | nsDocument::GetContentType(nsAString& aContentType) |
michael@0 | 3081 | { |
michael@0 | 3082 | CopyUTF8toUTF16(GetContentTypeInternal(), aContentType); |
michael@0 | 3083 | |
michael@0 | 3084 | return NS_OK; |
michael@0 | 3085 | } |
michael@0 | 3086 | |
michael@0 | 3087 | void |
michael@0 | 3088 | nsDocument::SetContentType(const nsAString& aContentType) |
michael@0 | 3089 | { |
michael@0 | 3090 | NS_ASSERTION(GetContentTypeInternal().IsEmpty() || |
michael@0 | 3091 | GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)), |
michael@0 | 3092 | "Do you really want to change the content-type?"); |
michael@0 | 3093 | |
michael@0 | 3094 | SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType)); |
michael@0 | 3095 | } |
michael@0 | 3096 | |
michael@0 | 3097 | nsresult |
michael@0 | 3098 | nsDocument::GetAllowPlugins(bool * aAllowPlugins) |
michael@0 | 3099 | { |
michael@0 | 3100 | // First, we ask our docshell if it allows plugins. |
michael@0 | 3101 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 3102 | |
michael@0 | 3103 | if (docShell) { |
michael@0 | 3104 | docShell->GetAllowPlugins(aAllowPlugins); |
michael@0 | 3105 | |
michael@0 | 3106 | // If the docshell allows plugins, we check whether |
michael@0 | 3107 | // we are sandboxed and plugins should not be allowed. |
michael@0 | 3108 | if (*aAllowPlugins) |
michael@0 | 3109 | *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS); |
michael@0 | 3110 | } |
michael@0 | 3111 | |
michael@0 | 3112 | return NS_OK; |
michael@0 | 3113 | } |
michael@0 | 3114 | |
michael@0 | 3115 | already_AddRefed<UndoManager> |
michael@0 | 3116 | nsDocument::GetUndoManager() |
michael@0 | 3117 | { |
michael@0 | 3118 | Element* rootElement = GetRootElement(); |
michael@0 | 3119 | if (!rootElement) { |
michael@0 | 3120 | return nullptr; |
michael@0 | 3121 | } |
michael@0 | 3122 | |
michael@0 | 3123 | if (!mUndoManager) { |
michael@0 | 3124 | mUndoManager = new UndoManager(rootElement); |
michael@0 | 3125 | } |
michael@0 | 3126 | |
michael@0 | 3127 | nsRefPtr<UndoManager> undoManager = mUndoManager; |
michael@0 | 3128 | return undoManager.forget(); |
michael@0 | 3129 | } |
michael@0 | 3130 | |
michael@0 | 3131 | /* Return true if the document is in the focused top-level window, and is an |
michael@0 | 3132 | * ancestor of the focused DOMWindow. */ |
michael@0 | 3133 | NS_IMETHODIMP |
michael@0 | 3134 | nsDocument::HasFocus(bool* aResult) |
michael@0 | 3135 | { |
michael@0 | 3136 | ErrorResult rv; |
michael@0 | 3137 | *aResult = nsIDocument::HasFocus(rv); |
michael@0 | 3138 | return rv.ErrorCode(); |
michael@0 | 3139 | } |
michael@0 | 3140 | |
michael@0 | 3141 | bool |
michael@0 | 3142 | nsIDocument::HasFocus(ErrorResult& rv) const |
michael@0 | 3143 | { |
michael@0 | 3144 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 3145 | if (!fm) { |
michael@0 | 3146 | rv.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 3147 | return false; |
michael@0 | 3148 | } |
michael@0 | 3149 | |
michael@0 | 3150 | // Is there a focused DOMWindow? |
michael@0 | 3151 | nsCOMPtr<nsIDOMWindow> focusedWindow; |
michael@0 | 3152 | fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); |
michael@0 | 3153 | if (!focusedWindow) { |
michael@0 | 3154 | return false; |
michael@0 | 3155 | } |
michael@0 | 3156 | |
michael@0 | 3157 | // Are we an ancestor of the focused DOMWindow? |
michael@0 | 3158 | nsCOMPtr<nsIDOMDocument> domDocument; |
michael@0 | 3159 | focusedWindow->GetDocument(getter_AddRefs(domDocument)); |
michael@0 | 3160 | nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument); |
michael@0 | 3161 | |
michael@0 | 3162 | for (nsIDocument* currentDoc = document; currentDoc; |
michael@0 | 3163 | currentDoc = currentDoc->GetParentDocument()) { |
michael@0 | 3164 | if (currentDoc == this) { |
michael@0 | 3165 | // Yes, we are an ancestor |
michael@0 | 3166 | return true; |
michael@0 | 3167 | } |
michael@0 | 3168 | } |
michael@0 | 3169 | |
michael@0 | 3170 | return false; |
michael@0 | 3171 | } |
michael@0 | 3172 | |
michael@0 | 3173 | NS_IMETHODIMP |
michael@0 | 3174 | nsDocument::GetReferrer(nsAString& aReferrer) |
michael@0 | 3175 | { |
michael@0 | 3176 | nsIDocument::GetReferrer(aReferrer); |
michael@0 | 3177 | return NS_OK; |
michael@0 | 3178 | } |
michael@0 | 3179 | |
michael@0 | 3180 | void |
michael@0 | 3181 | nsIDocument::GetReferrer(nsAString& aReferrer) const |
michael@0 | 3182 | { |
michael@0 | 3183 | if (mIsSrcdocDocument && mParentDocument) |
michael@0 | 3184 | mParentDocument->GetReferrer(aReferrer); |
michael@0 | 3185 | else |
michael@0 | 3186 | CopyUTF8toUTF16(mReferrer, aReferrer); |
michael@0 | 3187 | } |
michael@0 | 3188 | |
michael@0 | 3189 | nsresult |
michael@0 | 3190 | nsIDocument::GetSrcdocData(nsAString &aSrcdocData) |
michael@0 | 3191 | { |
michael@0 | 3192 | if (mIsSrcdocDocument) { |
michael@0 | 3193 | nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel); |
michael@0 | 3194 | if (inStrmChan) { |
michael@0 | 3195 | return inStrmChan->GetSrcdocData(aSrcdocData); |
michael@0 | 3196 | } |
michael@0 | 3197 | } |
michael@0 | 3198 | aSrcdocData = NullString(); |
michael@0 | 3199 | return NS_OK; |
michael@0 | 3200 | } |
michael@0 | 3201 | |
michael@0 | 3202 | NS_IMETHODIMP |
michael@0 | 3203 | nsDocument::GetActiveElement(nsIDOMElement **aElement) |
michael@0 | 3204 | { |
michael@0 | 3205 | nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement())); |
michael@0 | 3206 | el.forget(aElement); |
michael@0 | 3207 | return NS_OK; |
michael@0 | 3208 | } |
michael@0 | 3209 | |
michael@0 | 3210 | Element* |
michael@0 | 3211 | nsIDocument::GetActiveElement() |
michael@0 | 3212 | { |
michael@0 | 3213 | // Get the focused element. |
michael@0 | 3214 | nsCOMPtr<nsPIDOMWindow> window = GetWindow(); |
michael@0 | 3215 | if (window) { |
michael@0 | 3216 | nsCOMPtr<nsPIDOMWindow> focusedWindow; |
michael@0 | 3217 | nsIContent* focusedContent = |
michael@0 | 3218 | nsFocusManager::GetFocusedDescendant(window, false, |
michael@0 | 3219 | getter_AddRefs(focusedWindow)); |
michael@0 | 3220 | // be safe and make sure the element is from this document |
michael@0 | 3221 | if (focusedContent && focusedContent->OwnerDoc() == this) { |
michael@0 | 3222 | if (focusedContent->ChromeOnlyAccess()) { |
michael@0 | 3223 | focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); |
michael@0 | 3224 | } |
michael@0 | 3225 | if (focusedContent) { |
michael@0 | 3226 | return focusedContent->AsElement(); |
michael@0 | 3227 | } |
michael@0 | 3228 | } |
michael@0 | 3229 | } |
michael@0 | 3230 | |
michael@0 | 3231 | // No focused element anywhere in this document. Try to get the BODY. |
michael@0 | 3232 | nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument(); |
michael@0 | 3233 | if (htmlDoc) { |
michael@0 | 3234 | // Because of IE compatibility, return null when html document doesn't have |
michael@0 | 3235 | // a body. |
michael@0 | 3236 | return htmlDoc->GetBody(); |
michael@0 | 3237 | } |
michael@0 | 3238 | |
michael@0 | 3239 | // If we couldn't get a BODY, return the root element. |
michael@0 | 3240 | return GetDocumentElement(); |
michael@0 | 3241 | } |
michael@0 | 3242 | |
michael@0 | 3243 | NS_IMETHODIMP |
michael@0 | 3244 | nsDocument::GetCurrentScript(nsIDOMElement **aElement) |
michael@0 | 3245 | { |
michael@0 | 3246 | nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript())); |
michael@0 | 3247 | el.forget(aElement); |
michael@0 | 3248 | return NS_OK; |
michael@0 | 3249 | } |
michael@0 | 3250 | |
michael@0 | 3251 | Element* |
michael@0 | 3252 | nsIDocument::GetCurrentScript() |
michael@0 | 3253 | { |
michael@0 | 3254 | nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript())); |
michael@0 | 3255 | return el; |
michael@0 | 3256 | } |
michael@0 | 3257 | |
michael@0 | 3258 | NS_IMETHODIMP |
michael@0 | 3259 | nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn) |
michael@0 | 3260 | { |
michael@0 | 3261 | Element* el = nsIDocument::ElementFromPoint(aX, aY); |
michael@0 | 3262 | nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el); |
michael@0 | 3263 | retval.forget(aReturn); |
michael@0 | 3264 | return NS_OK; |
michael@0 | 3265 | } |
michael@0 | 3266 | |
michael@0 | 3267 | Element* |
michael@0 | 3268 | nsIDocument::ElementFromPoint(float aX, float aY) |
michael@0 | 3269 | { |
michael@0 | 3270 | return ElementFromPointHelper(aX, aY, false, true); |
michael@0 | 3271 | } |
michael@0 | 3272 | |
michael@0 | 3273 | Element* |
michael@0 | 3274 | nsDocument::ElementFromPointHelper(float aX, float aY, |
michael@0 | 3275 | bool aIgnoreRootScrollFrame, |
michael@0 | 3276 | bool aFlushLayout) |
michael@0 | 3277 | { |
michael@0 | 3278 | // As per the the spec, we return null if either coord is negative |
michael@0 | 3279 | if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) { |
michael@0 | 3280 | return nullptr; |
michael@0 | 3281 | } |
michael@0 | 3282 | |
michael@0 | 3283 | nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); |
michael@0 | 3284 | nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); |
michael@0 | 3285 | nsPoint pt(x, y); |
michael@0 | 3286 | |
michael@0 | 3287 | // Make sure the layout information we get is up-to-date, and |
michael@0 | 3288 | // ensure we get a root frame (for everything but XUL) |
michael@0 | 3289 | if (aFlushLayout) |
michael@0 | 3290 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 3291 | |
michael@0 | 3292 | nsIPresShell *ps = GetShell(); |
michael@0 | 3293 | if (!ps) { |
michael@0 | 3294 | return nullptr; |
michael@0 | 3295 | } |
michael@0 | 3296 | nsIFrame *rootFrame = ps->GetRootFrame(); |
michael@0 | 3297 | |
michael@0 | 3298 | // XUL docs, unlike HTML, have no frame tree until everything's done loading |
michael@0 | 3299 | if (!rootFrame) { |
michael@0 | 3300 | return nullptr; // return null to premature XUL callers as a reminder to wait |
michael@0 | 3301 | } |
michael@0 | 3302 | |
michael@0 | 3303 | nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, |
michael@0 | 3304 | nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | |
michael@0 | 3305 | (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); |
michael@0 | 3306 | if (!ptFrame) { |
michael@0 | 3307 | return nullptr; |
michael@0 | 3308 | } |
michael@0 | 3309 | |
michael@0 | 3310 | nsIContent* elem = GetContentInThisDocument(ptFrame); |
michael@0 | 3311 | if (elem && !elem->IsElement()) { |
michael@0 | 3312 | elem = elem->GetParent(); |
michael@0 | 3313 | } |
michael@0 | 3314 | return elem ? elem->AsElement() : nullptr; |
michael@0 | 3315 | } |
michael@0 | 3316 | |
michael@0 | 3317 | nsresult |
michael@0 | 3318 | nsDocument::NodesFromRectHelper(float aX, float aY, |
michael@0 | 3319 | float aTopSize, float aRightSize, |
michael@0 | 3320 | float aBottomSize, float aLeftSize, |
michael@0 | 3321 | bool aIgnoreRootScrollFrame, |
michael@0 | 3322 | bool aFlushLayout, |
michael@0 | 3323 | nsIDOMNodeList** aReturn) |
michael@0 | 3324 | { |
michael@0 | 3325 | NS_ENSURE_ARG_POINTER(aReturn); |
michael@0 | 3326 | |
michael@0 | 3327 | nsSimpleContentList* elements = new nsSimpleContentList(this); |
michael@0 | 3328 | NS_ADDREF(elements); |
michael@0 | 3329 | *aReturn = elements; |
michael@0 | 3330 | |
michael@0 | 3331 | // Following the same behavior of elementFromPoint, |
michael@0 | 3332 | // we don't return anything if either coord is negative |
michael@0 | 3333 | if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) |
michael@0 | 3334 | return NS_OK; |
michael@0 | 3335 | |
michael@0 | 3336 | nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize); |
michael@0 | 3337 | nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize); |
michael@0 | 3338 | nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1; |
michael@0 | 3339 | nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1; |
michael@0 | 3340 | |
michael@0 | 3341 | nsRect rect(x, y, w, h); |
michael@0 | 3342 | |
michael@0 | 3343 | // Make sure the layout information we get is up-to-date, and |
michael@0 | 3344 | // ensure we get a root frame (for everything but XUL) |
michael@0 | 3345 | if (aFlushLayout) { |
michael@0 | 3346 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 3347 | } |
michael@0 | 3348 | |
michael@0 | 3349 | nsIPresShell *ps = GetShell(); |
michael@0 | 3350 | NS_ENSURE_STATE(ps); |
michael@0 | 3351 | nsIFrame *rootFrame = ps->GetRootFrame(); |
michael@0 | 3352 | |
michael@0 | 3353 | // XUL docs, unlike HTML, have no frame tree until everything's done loading |
michael@0 | 3354 | if (!rootFrame) |
michael@0 | 3355 | return NS_OK; // return nothing to premature XUL callers as a reminder to wait |
michael@0 | 3356 | |
michael@0 | 3357 | nsAutoTArray<nsIFrame*,8> outFrames; |
michael@0 | 3358 | nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames, |
michael@0 | 3359 | nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | |
michael@0 | 3360 | (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); |
michael@0 | 3361 | |
michael@0 | 3362 | // Used to filter out repeated elements in sequence. |
michael@0 | 3363 | nsIContent* lastAdded = nullptr; |
michael@0 | 3364 | |
michael@0 | 3365 | for (uint32_t i = 0; i < outFrames.Length(); i++) { |
michael@0 | 3366 | nsIContent* node = GetContentInThisDocument(outFrames[i]); |
michael@0 | 3367 | |
michael@0 | 3368 | if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) { |
michael@0 | 3369 | // We have a node that isn't an element or a text node, |
michael@0 | 3370 | // use its parent content instead. |
michael@0 | 3371 | node = node->GetParent(); |
michael@0 | 3372 | } |
michael@0 | 3373 | if (node && node != lastAdded) { |
michael@0 | 3374 | elements->AppendElement(node); |
michael@0 | 3375 | lastAdded = node; |
michael@0 | 3376 | } |
michael@0 | 3377 | } |
michael@0 | 3378 | |
michael@0 | 3379 | return NS_OK; |
michael@0 | 3380 | } |
michael@0 | 3381 | |
michael@0 | 3382 | NS_IMETHODIMP |
michael@0 | 3383 | nsDocument::GetElementsByClassName(const nsAString& aClasses, |
michael@0 | 3384 | nsIDOMNodeList** aReturn) |
michael@0 | 3385 | { |
michael@0 | 3386 | *aReturn = nsIDocument::GetElementsByClassName(aClasses).take(); |
michael@0 | 3387 | return NS_OK; |
michael@0 | 3388 | } |
michael@0 | 3389 | |
michael@0 | 3390 | already_AddRefed<nsContentList> |
michael@0 | 3391 | nsIDocument::GetElementsByClassName(const nsAString& aClasses) |
michael@0 | 3392 | { |
michael@0 | 3393 | return nsContentUtils::GetElementsByClassName(this, aClasses); |
michael@0 | 3394 | } |
michael@0 | 3395 | |
michael@0 | 3396 | NS_IMETHODIMP |
michael@0 | 3397 | nsDocument::ReleaseCapture() |
michael@0 | 3398 | { |
michael@0 | 3399 | nsIDocument::ReleaseCapture(); |
michael@0 | 3400 | return NS_OK; |
michael@0 | 3401 | } |
michael@0 | 3402 | |
michael@0 | 3403 | void |
michael@0 | 3404 | nsIDocument::ReleaseCapture() const |
michael@0 | 3405 | { |
michael@0 | 3406 | // only release the capture if the caller can access it. This prevents a |
michael@0 | 3407 | // page from stopping a scrollbar grab for example. |
michael@0 | 3408 | nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent(); |
michael@0 | 3409 | if (node && nsContentUtils::CanCallerAccess(node)) { |
michael@0 | 3410 | nsIPresShell::SetCapturingContent(nullptr, 0); |
michael@0 | 3411 | } |
michael@0 | 3412 | } |
michael@0 | 3413 | |
michael@0 | 3414 | already_AddRefed<nsIURI> |
michael@0 | 3415 | nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const |
michael@0 | 3416 | { |
michael@0 | 3417 | nsCOMPtr<nsIURI> uri; |
michael@0 | 3418 | if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) { |
michael@0 | 3419 | uri = mChromeXHRDocBaseURI; |
michael@0 | 3420 | } else { |
michael@0 | 3421 | uri = GetDocBaseURI(); |
michael@0 | 3422 | } |
michael@0 | 3423 | |
michael@0 | 3424 | return uri.forget(); |
michael@0 | 3425 | } |
michael@0 | 3426 | |
michael@0 | 3427 | nsresult |
michael@0 | 3428 | nsDocument::SetBaseURI(nsIURI* aURI) |
michael@0 | 3429 | { |
michael@0 | 3430 | if (!aURI && !mDocumentBaseURI) { |
michael@0 | 3431 | return NS_OK; |
michael@0 | 3432 | } |
michael@0 | 3433 | |
michael@0 | 3434 | // Don't do anything if the URI wasn't actually changed. |
michael@0 | 3435 | if (aURI && mDocumentBaseURI) { |
michael@0 | 3436 | bool equalBases = false; |
michael@0 | 3437 | mDocumentBaseURI->Equals(aURI, &equalBases); |
michael@0 | 3438 | if (equalBases) { |
michael@0 | 3439 | return NS_OK; |
michael@0 | 3440 | } |
michael@0 | 3441 | } |
michael@0 | 3442 | |
michael@0 | 3443 | if (aURI) { |
michael@0 | 3444 | mDocumentBaseURI = NS_TryToMakeImmutable(aURI); |
michael@0 | 3445 | } else { |
michael@0 | 3446 | mDocumentBaseURI = nullptr; |
michael@0 | 3447 | } |
michael@0 | 3448 | RefreshLinkHrefs(); |
michael@0 | 3449 | |
michael@0 | 3450 | return NS_OK; |
michael@0 | 3451 | } |
michael@0 | 3452 | |
michael@0 | 3453 | void |
michael@0 | 3454 | nsDocument::GetBaseTarget(nsAString &aBaseTarget) |
michael@0 | 3455 | { |
michael@0 | 3456 | aBaseTarget = mBaseTarget; |
michael@0 | 3457 | } |
michael@0 | 3458 | |
michael@0 | 3459 | void |
michael@0 | 3460 | nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID) |
michael@0 | 3461 | { |
michael@0 | 3462 | // XXX it would be a good idea to assert the sanity of the argument, |
michael@0 | 3463 | // but before we figure out what to do about non-Encoding Standard |
michael@0 | 3464 | // encodings in the charset menu and in mailnews, assertions are futile. |
michael@0 | 3465 | if (!mCharacterSet.Equals(aCharSetID)) { |
michael@0 | 3466 | mCharacterSet = aCharSetID; |
michael@0 | 3467 | |
michael@0 | 3468 | int32_t n = mCharSetObservers.Length(); |
michael@0 | 3469 | |
michael@0 | 3470 | for (int32_t i = 0; i < n; i++) { |
michael@0 | 3471 | nsIObserver* observer = mCharSetObservers.ElementAt(i); |
michael@0 | 3472 | |
michael@0 | 3473 | observer->Observe(static_cast<nsIDocument *>(this), "charset", |
michael@0 | 3474 | NS_ConvertASCIItoUTF16(aCharSetID).get()); |
michael@0 | 3475 | } |
michael@0 | 3476 | } |
michael@0 | 3477 | } |
michael@0 | 3478 | |
michael@0 | 3479 | nsresult |
michael@0 | 3480 | nsDocument::AddCharSetObserver(nsIObserver* aObserver) |
michael@0 | 3481 | { |
michael@0 | 3482 | NS_ENSURE_ARG_POINTER(aObserver); |
michael@0 | 3483 | |
michael@0 | 3484 | NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE); |
michael@0 | 3485 | |
michael@0 | 3486 | return NS_OK; |
michael@0 | 3487 | } |
michael@0 | 3488 | |
michael@0 | 3489 | void |
michael@0 | 3490 | nsDocument::RemoveCharSetObserver(nsIObserver* aObserver) |
michael@0 | 3491 | { |
michael@0 | 3492 | mCharSetObservers.RemoveElement(aObserver); |
michael@0 | 3493 | } |
michael@0 | 3494 | |
michael@0 | 3495 | void |
michael@0 | 3496 | nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const |
michael@0 | 3497 | { |
michael@0 | 3498 | aData.Truncate(); |
michael@0 | 3499 | const nsDocHeaderData* data = mHeaderData; |
michael@0 | 3500 | while (data) { |
michael@0 | 3501 | if (data->mField == aHeaderField) { |
michael@0 | 3502 | aData = data->mData; |
michael@0 | 3503 | |
michael@0 | 3504 | break; |
michael@0 | 3505 | } |
michael@0 | 3506 | data = data->mNext; |
michael@0 | 3507 | } |
michael@0 | 3508 | } |
michael@0 | 3509 | |
michael@0 | 3510 | void |
michael@0 | 3511 | nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData) |
michael@0 | 3512 | { |
michael@0 | 3513 | if (!aHeaderField) { |
michael@0 | 3514 | NS_ERROR("null headerField"); |
michael@0 | 3515 | return; |
michael@0 | 3516 | } |
michael@0 | 3517 | |
michael@0 | 3518 | if (!mHeaderData) { |
michael@0 | 3519 | if (!aData.IsEmpty()) { // don't bother storing empty string |
michael@0 | 3520 | mHeaderData = new nsDocHeaderData(aHeaderField, aData); |
michael@0 | 3521 | } |
michael@0 | 3522 | } |
michael@0 | 3523 | else { |
michael@0 | 3524 | nsDocHeaderData* data = mHeaderData; |
michael@0 | 3525 | nsDocHeaderData** lastPtr = &mHeaderData; |
michael@0 | 3526 | bool found = false; |
michael@0 | 3527 | do { // look for existing and replace |
michael@0 | 3528 | if (data->mField == aHeaderField) { |
michael@0 | 3529 | if (!aData.IsEmpty()) { |
michael@0 | 3530 | data->mData.Assign(aData); |
michael@0 | 3531 | } |
michael@0 | 3532 | else { // don't store empty string |
michael@0 | 3533 | *lastPtr = data->mNext; |
michael@0 | 3534 | data->mNext = nullptr; |
michael@0 | 3535 | delete data; |
michael@0 | 3536 | } |
michael@0 | 3537 | found = true; |
michael@0 | 3538 | |
michael@0 | 3539 | break; |
michael@0 | 3540 | } |
michael@0 | 3541 | lastPtr = &(data->mNext); |
michael@0 | 3542 | data = *lastPtr; |
michael@0 | 3543 | } while (data); |
michael@0 | 3544 | |
michael@0 | 3545 | if (!aData.IsEmpty() && !found) { |
michael@0 | 3546 | // didn't find, append |
michael@0 | 3547 | *lastPtr = new nsDocHeaderData(aHeaderField, aData); |
michael@0 | 3548 | } |
michael@0 | 3549 | } |
michael@0 | 3550 | |
michael@0 | 3551 | if (aHeaderField == nsGkAtoms::headerContentLanguage) { |
michael@0 | 3552 | CopyUTF16toUTF8(aData, mContentLanguage); |
michael@0 | 3553 | } |
michael@0 | 3554 | |
michael@0 | 3555 | if (aHeaderField == nsGkAtoms::headerDefaultStyle) { |
michael@0 | 3556 | // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per |
michael@0 | 3557 | // spec. |
michael@0 | 3558 | if (DOMStringIsNull(mLastStyleSheetSet)) { |
michael@0 | 3559 | // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet, |
michael@0 | 3560 | // per spec. The idea here is that we're changing our preferred set and |
michael@0 | 3561 | // that shouldn't change the value of lastStyleSheetSet. Also, we're |
michael@0 | 3562 | // using the Internal version so we can update the CSSLoader and not have |
michael@0 | 3563 | // to worry about null strings. |
michael@0 | 3564 | EnableStyleSheetsForSetInternal(aData, true); |
michael@0 | 3565 | } |
michael@0 | 3566 | } |
michael@0 | 3567 | |
michael@0 | 3568 | if (aHeaderField == nsGkAtoms::refresh) { |
michael@0 | 3569 | // We get into this code before we have a script global yet, so get to |
michael@0 | 3570 | // our container via mDocumentContainer. |
michael@0 | 3571 | nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer); |
michael@0 | 3572 | if (refresher) { |
michael@0 | 3573 | // Note: using mDocumentURI instead of mBaseURI here, for consistency |
michael@0 | 3574 | // (used to just use the current URI of our webnavigation, but that |
michael@0 | 3575 | // should really be the same thing). Note that this code can run |
michael@0 | 3576 | // before the current URI of the webnavigation has been updated, so we |
michael@0 | 3577 | // can't assert equality here. |
michael@0 | 3578 | refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(), |
michael@0 | 3579 | NS_ConvertUTF16toUTF8(aData)); |
michael@0 | 3580 | } |
michael@0 | 3581 | } |
michael@0 | 3582 | |
michael@0 | 3583 | if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl && |
michael@0 | 3584 | mAllowDNSPrefetch) { |
michael@0 | 3585 | // Chromium treats any value other than 'on' (case insensitive) as 'off'. |
michael@0 | 3586 | mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on"); |
michael@0 | 3587 | } |
michael@0 | 3588 | |
michael@0 | 3589 | if (aHeaderField == nsGkAtoms::viewport || |
michael@0 | 3590 | aHeaderField == nsGkAtoms::handheldFriendly || |
michael@0 | 3591 | aHeaderField == nsGkAtoms::viewport_minimum_scale || |
michael@0 | 3592 | aHeaderField == nsGkAtoms::viewport_maximum_scale || |
michael@0 | 3593 | aHeaderField == nsGkAtoms::viewport_initial_scale || |
michael@0 | 3594 | aHeaderField == nsGkAtoms::viewport_height || |
michael@0 | 3595 | aHeaderField == nsGkAtoms::viewport_width || |
michael@0 | 3596 | aHeaderField == nsGkAtoms::viewport_user_scalable) { |
michael@0 | 3597 | mViewportType = Unknown; |
michael@0 | 3598 | } |
michael@0 | 3599 | } |
michael@0 | 3600 | |
michael@0 | 3601 | void |
michael@0 | 3602 | nsDocument::TryChannelCharset(nsIChannel *aChannel, |
michael@0 | 3603 | int32_t& aCharsetSource, |
michael@0 | 3604 | nsACString& aCharset, |
michael@0 | 3605 | nsHtml5TreeOpExecutor* aExecutor) |
michael@0 | 3606 | { |
michael@0 | 3607 | if (aChannel) { |
michael@0 | 3608 | nsAutoCString charsetVal; |
michael@0 | 3609 | nsresult rv = aChannel->GetContentCharset(charsetVal); |
michael@0 | 3610 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 3611 | nsAutoCString preferred; |
michael@0 | 3612 | if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) { |
michael@0 | 3613 | aCharset = preferred; |
michael@0 | 3614 | aCharsetSource = kCharsetFromChannel; |
michael@0 | 3615 | return; |
michael@0 | 3616 | } else if (aExecutor && !charsetVal.IsEmpty()) { |
michael@0 | 3617 | aExecutor->ComplainAboutBogusProtocolCharset(this); |
michael@0 | 3618 | } |
michael@0 | 3619 | } |
michael@0 | 3620 | } |
michael@0 | 3621 | } |
michael@0 | 3622 | |
michael@0 | 3623 | already_AddRefed<nsIPresShell> |
michael@0 | 3624 | nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager, |
michael@0 | 3625 | nsStyleSet* aStyleSet) |
michael@0 | 3626 | { |
michael@0 | 3627 | // Don't add anything here. Add it to |doCreateShell| instead. |
michael@0 | 3628 | // This exists so that subclasses can pass other values for the 4th |
michael@0 | 3629 | // parameter some of the time. |
michael@0 | 3630 | return doCreateShell(aContext, aViewManager, aStyleSet, |
michael@0 | 3631 | eCompatibility_FullStandards); |
michael@0 | 3632 | } |
michael@0 | 3633 | |
michael@0 | 3634 | already_AddRefed<nsIPresShell> |
michael@0 | 3635 | nsDocument::doCreateShell(nsPresContext* aContext, |
michael@0 | 3636 | nsViewManager* aViewManager, nsStyleSet* aStyleSet, |
michael@0 | 3637 | nsCompatibility aCompatMode) |
michael@0 | 3638 | { |
michael@0 | 3639 | NS_ASSERTION(!mPresShell, "We have a presshell already!"); |
michael@0 | 3640 | |
michael@0 | 3641 | NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr); |
michael@0 | 3642 | |
michael@0 | 3643 | FillStyleSet(aStyleSet); |
michael@0 | 3644 | |
michael@0 | 3645 | nsRefPtr<PresShell> shell = new PresShell; |
michael@0 | 3646 | shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode); |
michael@0 | 3647 | |
michael@0 | 3648 | // Note: we don't hold a ref to the shell (it holds a ref to us) |
michael@0 | 3649 | mPresShell = shell; |
michael@0 | 3650 | |
michael@0 | 3651 | // Make sure to never paint if we belong to an invisible DocShell. |
michael@0 | 3652 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 3653 | if (docShell && docShell->IsInvisible()) |
michael@0 | 3654 | shell->SetNeverPainting(true); |
michael@0 | 3655 | |
michael@0 | 3656 | mExternalResourceMap.ShowViewers(); |
michael@0 | 3657 | |
michael@0 | 3658 | MaybeRescheduleAnimationFrameNotifications(); |
michael@0 | 3659 | |
michael@0 | 3660 | return shell.forget(); |
michael@0 | 3661 | } |
michael@0 | 3662 | |
michael@0 | 3663 | void |
michael@0 | 3664 | nsDocument::MaybeRescheduleAnimationFrameNotifications() |
michael@0 | 3665 | { |
michael@0 | 3666 | if (!mPresShell || !IsEventHandlingEnabled()) { |
michael@0 | 3667 | // bail out for now, until one of those conditions changes |
michael@0 | 3668 | return; |
michael@0 | 3669 | } |
michael@0 | 3670 | |
michael@0 | 3671 | nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver(); |
michael@0 | 3672 | if (!mFrameRequestCallbacks.IsEmpty()) { |
michael@0 | 3673 | rd->ScheduleFrameRequestCallbacks(this); |
michael@0 | 3674 | } |
michael@0 | 3675 | } |
michael@0 | 3676 | |
michael@0 | 3677 | void |
michael@0 | 3678 | nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks) |
michael@0 | 3679 | { |
michael@0 | 3680 | aCallbacks.AppendElements(mFrameRequestCallbacks); |
michael@0 | 3681 | mFrameRequestCallbacks.Clear(); |
michael@0 | 3682 | } |
michael@0 | 3683 | |
michael@0 | 3684 | PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey, |
michael@0 | 3685 | uint32_t aData, |
michael@0 | 3686 | void* userArg) |
michael@0 | 3687 | { |
michael@0 | 3688 | aKey->RequestDiscard(); |
michael@0 | 3689 | return PL_DHASH_NEXT; |
michael@0 | 3690 | } |
michael@0 | 3691 | |
michael@0 | 3692 | void |
michael@0 | 3693 | nsDocument::DeleteShell() |
michael@0 | 3694 | { |
michael@0 | 3695 | mExternalResourceMap.HideViewers(); |
michael@0 | 3696 | if (IsEventHandlingEnabled()) { |
michael@0 | 3697 | RevokeAnimationFrameNotifications(); |
michael@0 | 3698 | } |
michael@0 | 3699 | |
michael@0 | 3700 | // When our shell goes away, request that all our images be immediately |
michael@0 | 3701 | // discarded, so we don't carry around decoded image data for a document we |
michael@0 | 3702 | // no longer intend to paint. |
michael@0 | 3703 | mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr); |
michael@0 | 3704 | |
michael@0 | 3705 | mPresShell = nullptr; |
michael@0 | 3706 | } |
michael@0 | 3707 | |
michael@0 | 3708 | void |
michael@0 | 3709 | nsDocument::RevokeAnimationFrameNotifications() |
michael@0 | 3710 | { |
michael@0 | 3711 | if (!mFrameRequestCallbacks.IsEmpty()) { |
michael@0 | 3712 | mPresShell->GetPresContext()->RefreshDriver()-> |
michael@0 | 3713 | RevokeFrameRequestCallbacks(this); |
michael@0 | 3714 | } |
michael@0 | 3715 | } |
michael@0 | 3716 | |
michael@0 | 3717 | static void |
michael@0 | 3718 | SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) |
michael@0 | 3719 | { |
michael@0 | 3720 | SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry); |
michael@0 | 3721 | |
michael@0 | 3722 | NS_RELEASE(e->mKey); |
michael@0 | 3723 | if (e->mSubDocument) { |
michael@0 | 3724 | e->mSubDocument->SetParentDocument(nullptr); |
michael@0 | 3725 | NS_RELEASE(e->mSubDocument); |
michael@0 | 3726 | } |
michael@0 | 3727 | } |
michael@0 | 3728 | |
michael@0 | 3729 | static bool |
michael@0 | 3730 | SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key) |
michael@0 | 3731 | { |
michael@0 | 3732 | SubDocMapEntry *e = |
michael@0 | 3733 | const_cast<SubDocMapEntry *> |
michael@0 | 3734 | (static_cast<const SubDocMapEntry *>(entry)); |
michael@0 | 3735 | |
michael@0 | 3736 | e->mKey = const_cast<Element*>(static_cast<const Element*>(key)); |
michael@0 | 3737 | NS_ADDREF(e->mKey); |
michael@0 | 3738 | |
michael@0 | 3739 | e->mSubDocument = nullptr; |
michael@0 | 3740 | return true; |
michael@0 | 3741 | } |
michael@0 | 3742 | |
michael@0 | 3743 | nsresult |
michael@0 | 3744 | nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc) |
michael@0 | 3745 | { |
michael@0 | 3746 | NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED); |
michael@0 | 3747 | |
michael@0 | 3748 | if (!aSubDoc) { |
michael@0 | 3749 | // aSubDoc is nullptr, remove the mapping |
michael@0 | 3750 | |
michael@0 | 3751 | if (mSubDocuments) { |
michael@0 | 3752 | SubDocMapEntry *entry = |
michael@0 | 3753 | static_cast<SubDocMapEntry*> |
michael@0 | 3754 | (PL_DHashTableOperate(mSubDocuments, aElement, |
michael@0 | 3755 | PL_DHASH_LOOKUP)); |
michael@0 | 3756 | |
michael@0 | 3757 | if (PL_DHASH_ENTRY_IS_BUSY(entry)) { |
michael@0 | 3758 | PL_DHashTableRawRemove(mSubDocuments, entry); |
michael@0 | 3759 | } |
michael@0 | 3760 | } |
michael@0 | 3761 | } else { |
michael@0 | 3762 | if (!mSubDocuments) { |
michael@0 | 3763 | // Create a new hashtable |
michael@0 | 3764 | |
michael@0 | 3765 | static const PLDHashTableOps hash_table_ops = |
michael@0 | 3766 | { |
michael@0 | 3767 | PL_DHashAllocTable, |
michael@0 | 3768 | PL_DHashFreeTable, |
michael@0 | 3769 | PL_DHashVoidPtrKeyStub, |
michael@0 | 3770 | PL_DHashMatchEntryStub, |
michael@0 | 3771 | PL_DHashMoveEntryStub, |
michael@0 | 3772 | SubDocClearEntry, |
michael@0 | 3773 | PL_DHashFinalizeStub, |
michael@0 | 3774 | SubDocInitEntry |
michael@0 | 3775 | }; |
michael@0 | 3776 | |
michael@0 | 3777 | mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr, |
michael@0 | 3778 | sizeof(SubDocMapEntry), 16); |
michael@0 | 3779 | if (!mSubDocuments) { |
michael@0 | 3780 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 3781 | } |
michael@0 | 3782 | } |
michael@0 | 3783 | |
michael@0 | 3784 | // Add a mapping to the hash table |
michael@0 | 3785 | SubDocMapEntry *entry = |
michael@0 | 3786 | static_cast<SubDocMapEntry*> |
michael@0 | 3787 | (PL_DHashTableOperate(mSubDocuments, aElement, |
michael@0 | 3788 | PL_DHASH_ADD)); |
michael@0 | 3789 | |
michael@0 | 3790 | if (!entry) { |
michael@0 | 3791 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 3792 | } |
michael@0 | 3793 | |
michael@0 | 3794 | if (entry->mSubDocument) { |
michael@0 | 3795 | entry->mSubDocument->SetParentDocument(nullptr); |
michael@0 | 3796 | |
michael@0 | 3797 | // Release the old sub document |
michael@0 | 3798 | NS_RELEASE(entry->mSubDocument); |
michael@0 | 3799 | } |
michael@0 | 3800 | |
michael@0 | 3801 | entry->mSubDocument = aSubDoc; |
michael@0 | 3802 | NS_ADDREF(entry->mSubDocument); |
michael@0 | 3803 | |
michael@0 | 3804 | aSubDoc->SetParentDocument(this); |
michael@0 | 3805 | } |
michael@0 | 3806 | |
michael@0 | 3807 | return NS_OK; |
michael@0 | 3808 | } |
michael@0 | 3809 | |
michael@0 | 3810 | nsIDocument* |
michael@0 | 3811 | nsDocument::GetSubDocumentFor(nsIContent *aContent) const |
michael@0 | 3812 | { |
michael@0 | 3813 | if (mSubDocuments && aContent->IsElement()) { |
michael@0 | 3814 | SubDocMapEntry *entry = |
michael@0 | 3815 | static_cast<SubDocMapEntry*> |
michael@0 | 3816 | (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(), |
michael@0 | 3817 | PL_DHASH_LOOKUP)); |
michael@0 | 3818 | |
michael@0 | 3819 | if (PL_DHASH_ENTRY_IS_BUSY(entry)) { |
michael@0 | 3820 | return entry->mSubDocument; |
michael@0 | 3821 | } |
michael@0 | 3822 | } |
michael@0 | 3823 | |
michael@0 | 3824 | return nullptr; |
michael@0 | 3825 | } |
michael@0 | 3826 | |
michael@0 | 3827 | static PLDHashOperator |
michael@0 | 3828 | FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, |
michael@0 | 3829 | uint32_t number, void *arg) |
michael@0 | 3830 | { |
michael@0 | 3831 | SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
michael@0 | 3832 | FindContentData *data = static_cast<FindContentData*>(arg); |
michael@0 | 3833 | |
michael@0 | 3834 | if (entry->mSubDocument == data->mSubDocument) { |
michael@0 | 3835 | data->mResult = entry->mKey; |
michael@0 | 3836 | |
michael@0 | 3837 | return PL_DHASH_STOP; |
michael@0 | 3838 | } |
michael@0 | 3839 | |
michael@0 | 3840 | return PL_DHASH_NEXT; |
michael@0 | 3841 | } |
michael@0 | 3842 | |
michael@0 | 3843 | Element* |
michael@0 | 3844 | nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const |
michael@0 | 3845 | { |
michael@0 | 3846 | NS_ENSURE_TRUE(aDocument, nullptr); |
michael@0 | 3847 | |
michael@0 | 3848 | if (!mSubDocuments) { |
michael@0 | 3849 | return nullptr; |
michael@0 | 3850 | } |
michael@0 | 3851 | |
michael@0 | 3852 | FindContentData data(aDocument); |
michael@0 | 3853 | PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data); |
michael@0 | 3854 | |
michael@0 | 3855 | return data.mResult; |
michael@0 | 3856 | } |
michael@0 | 3857 | |
michael@0 | 3858 | bool |
michael@0 | 3859 | nsDocument::IsNodeOfType(uint32_t aFlags) const |
michael@0 | 3860 | { |
michael@0 | 3861 | return !(aFlags & ~eDOCUMENT); |
michael@0 | 3862 | } |
michael@0 | 3863 | |
michael@0 | 3864 | Element* |
michael@0 | 3865 | nsIDocument::GetRootElement() const |
michael@0 | 3866 | { |
michael@0 | 3867 | return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ? |
michael@0 | 3868 | mCachedRootElement : GetRootElementInternal(); |
michael@0 | 3869 | } |
michael@0 | 3870 | |
michael@0 | 3871 | Element* |
michael@0 | 3872 | nsDocument::GetRootElementInternal() const |
michael@0 | 3873 | { |
michael@0 | 3874 | // Loop backwards because any non-elements, such as doctypes and PIs |
michael@0 | 3875 | // are likely to appear before the root element. |
michael@0 | 3876 | uint32_t i; |
michael@0 | 3877 | for (i = mChildren.ChildCount(); i > 0; --i) { |
michael@0 | 3878 | nsIContent* child = mChildren.ChildAt(i - 1); |
michael@0 | 3879 | if (child->IsElement()) { |
michael@0 | 3880 | const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement(); |
michael@0 | 3881 | return child->AsElement(); |
michael@0 | 3882 | } |
michael@0 | 3883 | } |
michael@0 | 3884 | |
michael@0 | 3885 | const_cast<nsDocument*>(this)->mCachedRootElement = nullptr; |
michael@0 | 3886 | return nullptr; |
michael@0 | 3887 | } |
michael@0 | 3888 | |
michael@0 | 3889 | nsIContent * |
michael@0 | 3890 | nsDocument::GetChildAt(uint32_t aIndex) const |
michael@0 | 3891 | { |
michael@0 | 3892 | return mChildren.GetSafeChildAt(aIndex); |
michael@0 | 3893 | } |
michael@0 | 3894 | |
michael@0 | 3895 | int32_t |
michael@0 | 3896 | nsDocument::IndexOf(const nsINode* aPossibleChild) const |
michael@0 | 3897 | { |
michael@0 | 3898 | return mChildren.IndexOfChild(aPossibleChild); |
michael@0 | 3899 | } |
michael@0 | 3900 | |
michael@0 | 3901 | uint32_t |
michael@0 | 3902 | nsDocument::GetChildCount() const |
michael@0 | 3903 | { |
michael@0 | 3904 | return mChildren.ChildCount(); |
michael@0 | 3905 | } |
michael@0 | 3906 | |
michael@0 | 3907 | nsIContent * const * |
michael@0 | 3908 | nsDocument::GetChildArray(uint32_t* aChildCount) const |
michael@0 | 3909 | { |
michael@0 | 3910 | return mChildren.GetChildArray(aChildCount); |
michael@0 | 3911 | } |
michael@0 | 3912 | |
michael@0 | 3913 | |
michael@0 | 3914 | nsresult |
michael@0 | 3915 | nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex, |
michael@0 | 3916 | bool aNotify) |
michael@0 | 3917 | { |
michael@0 | 3918 | if (aKid->IsElement() && GetRootElement()) { |
michael@0 | 3919 | NS_WARNING("Inserting root element when we already have one"); |
michael@0 | 3920 | return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
michael@0 | 3921 | } |
michael@0 | 3922 | |
michael@0 | 3923 | return doInsertChildAt(aKid, aIndex, aNotify, mChildren); |
michael@0 | 3924 | } |
michael@0 | 3925 | |
michael@0 | 3926 | nsresult |
michael@0 | 3927 | nsDocument::AppendChildTo(nsIContent* aKid, bool aNotify) |
michael@0 | 3928 | { |
michael@0 | 3929 | // Make sure to _not_ call the subclass InsertChildAt here. If |
michael@0 | 3930 | // subclasses wanted to hook into this stuff, they would have |
michael@0 | 3931 | // overridden AppendChildTo. |
michael@0 | 3932 | // XXXbz maybe this should just be a non-virtual method on nsINode? |
michael@0 | 3933 | // Feels that way to me... |
michael@0 | 3934 | return nsDocument::InsertChildAt(aKid, GetChildCount(), aNotify); |
michael@0 | 3935 | } |
michael@0 | 3936 | |
michael@0 | 3937 | void |
michael@0 | 3938 | nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify) |
michael@0 | 3939 | { |
michael@0 | 3940 | nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex); |
michael@0 | 3941 | if (!oldKid) { |
michael@0 | 3942 | return; |
michael@0 | 3943 | } |
michael@0 | 3944 | |
michael@0 | 3945 | if (oldKid->IsElement()) { |
michael@0 | 3946 | // Destroy the link map up front before we mess with the child list. |
michael@0 | 3947 | DestroyElementMaps(); |
michael@0 | 3948 | } |
michael@0 | 3949 | |
michael@0 | 3950 | doRemoveChildAt(aIndex, aNotify, oldKid, mChildren); |
michael@0 | 3951 | mCachedRootElement = nullptr; |
michael@0 | 3952 | } |
michael@0 | 3953 | |
michael@0 | 3954 | int32_t |
michael@0 | 3955 | nsDocument::GetNumberOfStyleSheets() const |
michael@0 | 3956 | { |
michael@0 | 3957 | return mStyleSheets.Count(); |
michael@0 | 3958 | } |
michael@0 | 3959 | |
michael@0 | 3960 | nsIStyleSheet* |
michael@0 | 3961 | nsDocument::GetStyleSheetAt(int32_t aIndex) const |
michael@0 | 3962 | { |
michael@0 | 3963 | NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr); |
michael@0 | 3964 | return mStyleSheets[aIndex]; |
michael@0 | 3965 | } |
michael@0 | 3966 | |
michael@0 | 3967 | int32_t |
michael@0 | 3968 | nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const |
michael@0 | 3969 | { |
michael@0 | 3970 | return mStyleSheets.IndexOf(aSheet); |
michael@0 | 3971 | } |
michael@0 | 3972 | |
michael@0 | 3973 | void |
michael@0 | 3974 | nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet) |
michael@0 | 3975 | { |
michael@0 | 3976 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 3977 | if (shell) { |
michael@0 | 3978 | shell->StyleSet()->AddDocStyleSheet(aSheet, this); |
michael@0 | 3979 | } |
michael@0 | 3980 | } |
michael@0 | 3981 | |
michael@0 | 3982 | #define DO_STYLESHEET_NOTIFICATION(createFunc, concreteInterface, initMethod, type, ...) \ |
michael@0 | 3983 | do { \ |
michael@0 | 3984 | nsCOMPtr<nsIDOMEvent> event; \ |
michael@0 | 3985 | nsresult rv = createFunc(getter_AddRefs(event), this, \ |
michael@0 | 3986 | mPresShell ? \ |
michael@0 | 3987 | mPresShell->GetPresContext() : nullptr, \ |
michael@0 | 3988 | nullptr); \ |
michael@0 | 3989 | if (NS_FAILED(rv)) { \ |
michael@0 | 3990 | return; \ |
michael@0 | 3991 | } \ |
michael@0 | 3992 | nsCOMPtr<nsIDOMCSSStyleSheet> cssSheet(do_QueryInterface(aSheet)); \ |
michael@0 | 3993 | if (!cssSheet) { \ |
michael@0 | 3994 | return; \ |
michael@0 | 3995 | } \ |
michael@0 | 3996 | nsCOMPtr<concreteInterface> ssEvent(do_QueryInterface(event)); \ |
michael@0 | 3997 | MOZ_ASSERT(ssEvent); \ |
michael@0 | 3998 | ssEvent->initMethod(NS_LITERAL_STRING(type), true, true, \ |
michael@0 | 3999 | cssSheet, __VA_ARGS__); \ |
michael@0 | 4000 | event->SetTrusted(true); \ |
michael@0 | 4001 | event->SetTarget(this); \ |
michael@0 | 4002 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = \ |
michael@0 | 4003 | new AsyncEventDispatcher(this, event); \ |
michael@0 | 4004 | asyncDispatcher->mDispatchChromeOnly = true; \ |
michael@0 | 4005 | asyncDispatcher->PostDOMEvent(); \ |
michael@0 | 4006 | } while (0); |
michael@0 | 4007 | |
michael@0 | 4008 | void |
michael@0 | 4009 | nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet) |
michael@0 | 4010 | { |
michael@0 | 4011 | NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet)); |
michael@0 | 4012 | |
michael@0 | 4013 | if (StyleSheetChangeEventsEnabled()) { |
michael@0 | 4014 | DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent, |
michael@0 | 4015 | nsIDOMStyleSheetChangeEvent, |
michael@0 | 4016 | InitStyleSheetChangeEvent, |
michael@0 | 4017 | "StyleSheetAdded", |
michael@0 | 4018 | aDocumentSheet); |
michael@0 | 4019 | } |
michael@0 | 4020 | } |
michael@0 | 4021 | |
michael@0 | 4022 | void |
michael@0 | 4023 | nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet) |
michael@0 | 4024 | { |
michael@0 | 4025 | NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet)); |
michael@0 | 4026 | |
michael@0 | 4027 | if (StyleSheetChangeEventsEnabled()) { |
michael@0 | 4028 | DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent, |
michael@0 | 4029 | nsIDOMStyleSheetChangeEvent, |
michael@0 | 4030 | InitStyleSheetChangeEvent, |
michael@0 | 4031 | "StyleSheetRemoved", |
michael@0 | 4032 | aDocumentSheet); |
michael@0 | 4033 | } |
michael@0 | 4034 | } |
michael@0 | 4035 | |
michael@0 | 4036 | void |
michael@0 | 4037 | nsDocument::AddStyleSheet(nsIStyleSheet* aSheet) |
michael@0 | 4038 | { |
michael@0 | 4039 | NS_PRECONDITION(aSheet, "null arg"); |
michael@0 | 4040 | mStyleSheets.AppendObject(aSheet); |
michael@0 | 4041 | aSheet->SetOwningDocument(this); |
michael@0 | 4042 | |
michael@0 | 4043 | if (aSheet->IsApplicable()) { |
michael@0 | 4044 | AddStyleSheetToStyleSets(aSheet); |
michael@0 | 4045 | } |
michael@0 | 4046 | |
michael@0 | 4047 | NotifyStyleSheetAdded(aSheet, true); |
michael@0 | 4048 | } |
michael@0 | 4049 | |
michael@0 | 4050 | void |
michael@0 | 4051 | nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet) |
michael@0 | 4052 | { |
michael@0 | 4053 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 4054 | if (shell) { |
michael@0 | 4055 | shell->StyleSet()->RemoveDocStyleSheet(aSheet); |
michael@0 | 4056 | } |
michael@0 | 4057 | } |
michael@0 | 4058 | |
michael@0 | 4059 | void |
michael@0 | 4060 | nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet) |
michael@0 | 4061 | { |
michael@0 | 4062 | NS_PRECONDITION(aSheet, "null arg"); |
michael@0 | 4063 | nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon |
michael@0 | 4064 | |
michael@0 | 4065 | if (!mStyleSheets.RemoveObject(aSheet)) { |
michael@0 | 4066 | NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found"); |
michael@0 | 4067 | return; |
michael@0 | 4068 | } |
michael@0 | 4069 | |
michael@0 | 4070 | if (!mIsGoingAway) { |
michael@0 | 4071 | if (aSheet->IsApplicable()) { |
michael@0 | 4072 | RemoveStyleSheetFromStyleSets(aSheet); |
michael@0 | 4073 | } |
michael@0 | 4074 | |
michael@0 | 4075 | NotifyStyleSheetRemoved(aSheet, true); |
michael@0 | 4076 | } |
michael@0 | 4077 | |
michael@0 | 4078 | aSheet->SetOwningDocument(nullptr); |
michael@0 | 4079 | } |
michael@0 | 4080 | |
michael@0 | 4081 | void |
michael@0 | 4082 | nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets, |
michael@0 | 4083 | nsCOMArray<nsIStyleSheet>& aNewSheets) |
michael@0 | 4084 | { |
michael@0 | 4085 | BeginUpdate(UPDATE_STYLE); |
michael@0 | 4086 | |
michael@0 | 4087 | // XXX Need to set the sheet on the ownernode, if any |
michael@0 | 4088 | NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(), |
michael@0 | 4089 | "The lists must be the same length!"); |
michael@0 | 4090 | int32_t count = aOldSheets.Count(); |
michael@0 | 4091 | |
michael@0 | 4092 | nsCOMPtr<nsIStyleSheet> oldSheet; |
michael@0 | 4093 | int32_t i; |
michael@0 | 4094 | for (i = 0; i < count; ++i) { |
michael@0 | 4095 | oldSheet = aOldSheets[i]; |
michael@0 | 4096 | |
michael@0 | 4097 | // First remove the old sheet. |
michael@0 | 4098 | NS_ASSERTION(oldSheet, "None of the old sheets should be null"); |
michael@0 | 4099 | int32_t oldIndex = mStyleSheets.IndexOf(oldSheet); |
michael@0 | 4100 | RemoveStyleSheet(oldSheet); // This does the right notifications |
michael@0 | 4101 | |
michael@0 | 4102 | // Now put the new one in its place. If it's null, just ignore it. |
michael@0 | 4103 | nsIStyleSheet* newSheet = aNewSheets[i]; |
michael@0 | 4104 | if (newSheet) { |
michael@0 | 4105 | mStyleSheets.InsertObjectAt(newSheet, oldIndex); |
michael@0 | 4106 | newSheet->SetOwningDocument(this); |
michael@0 | 4107 | if (newSheet->IsApplicable()) { |
michael@0 | 4108 | AddStyleSheetToStyleSets(newSheet); |
michael@0 | 4109 | } |
michael@0 | 4110 | |
michael@0 | 4111 | NotifyStyleSheetAdded(newSheet, true); |
michael@0 | 4112 | } |
michael@0 | 4113 | } |
michael@0 | 4114 | |
michael@0 | 4115 | EndUpdate(UPDATE_STYLE); |
michael@0 | 4116 | } |
michael@0 | 4117 | |
michael@0 | 4118 | void |
michael@0 | 4119 | nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex) |
michael@0 | 4120 | { |
michael@0 | 4121 | NS_PRECONDITION(aSheet, "null ptr"); |
michael@0 | 4122 | mStyleSheets.InsertObjectAt(aSheet, aIndex); |
michael@0 | 4123 | |
michael@0 | 4124 | aSheet->SetOwningDocument(this); |
michael@0 | 4125 | |
michael@0 | 4126 | if (aSheet->IsApplicable()) { |
michael@0 | 4127 | AddStyleSheetToStyleSets(aSheet); |
michael@0 | 4128 | } |
michael@0 | 4129 | |
michael@0 | 4130 | NotifyStyleSheetAdded(aSheet, true); |
michael@0 | 4131 | } |
michael@0 | 4132 | |
michael@0 | 4133 | |
michael@0 | 4134 | void |
michael@0 | 4135 | nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet, |
michael@0 | 4136 | bool aApplicable) |
michael@0 | 4137 | { |
michael@0 | 4138 | NS_PRECONDITION(aSheet, "null arg"); |
michael@0 | 4139 | |
michael@0 | 4140 | // If we're actually in the document style sheet list |
michael@0 | 4141 | if (-1 != mStyleSheets.IndexOf(aSheet)) { |
michael@0 | 4142 | if (aApplicable) { |
michael@0 | 4143 | AddStyleSheetToStyleSets(aSheet); |
michael@0 | 4144 | } else { |
michael@0 | 4145 | RemoveStyleSheetFromStyleSets(aSheet); |
michael@0 | 4146 | } |
michael@0 | 4147 | } |
michael@0 | 4148 | |
michael@0 | 4149 | // We have to always notify, since this will be called for sheets |
michael@0 | 4150 | // that are children of sheets in our style set, as well as some |
michael@0 | 4151 | // sheets for nsHTMLEditor. |
michael@0 | 4152 | |
michael@0 | 4153 | NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, |
michael@0 | 4154 | (this, aSheet, aApplicable)); |
michael@0 | 4155 | |
michael@0 | 4156 | if (StyleSheetChangeEventsEnabled()) { |
michael@0 | 4157 | DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetApplicableStateChangeEvent, |
michael@0 | 4158 | nsIDOMStyleSheetApplicableStateChangeEvent, |
michael@0 | 4159 | InitStyleSheetApplicableStateChangeEvent, |
michael@0 | 4160 | "StyleSheetApplicableStateChanged", |
michael@0 | 4161 | aApplicable); |
michael@0 | 4162 | } |
michael@0 | 4163 | |
michael@0 | 4164 | if (!mSSApplicableStateNotificationPending) { |
michael@0 | 4165 | nsRefPtr<nsIRunnable> notification = NS_NewRunnableMethod(this, |
michael@0 | 4166 | &nsDocument::NotifyStyleSheetApplicableStateChanged); |
michael@0 | 4167 | mSSApplicableStateNotificationPending = |
michael@0 | 4168 | NS_SUCCEEDED(NS_DispatchToCurrentThread(notification)); |
michael@0 | 4169 | } |
michael@0 | 4170 | } |
michael@0 | 4171 | |
michael@0 | 4172 | void |
michael@0 | 4173 | nsDocument::NotifyStyleSheetApplicableStateChanged() |
michael@0 | 4174 | { |
michael@0 | 4175 | mSSApplicableStateNotificationPending = false; |
michael@0 | 4176 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 4177 | mozilla::services::GetObserverService(); |
michael@0 | 4178 | if (observerService) { |
michael@0 | 4179 | observerService->NotifyObservers(static_cast<nsIDocument*>(this), |
michael@0 | 4180 | "style-sheet-applicable-state-changed", |
michael@0 | 4181 | nullptr); |
michael@0 | 4182 | } |
michael@0 | 4183 | } |
michael@0 | 4184 | |
michael@0 | 4185 | // These three functions are a lot like the implementation of the |
michael@0 | 4186 | // corresponding API for regular stylesheets. |
michael@0 | 4187 | |
michael@0 | 4188 | int32_t |
michael@0 | 4189 | nsDocument::GetNumberOfCatalogStyleSheets() const |
michael@0 | 4190 | { |
michael@0 | 4191 | return mCatalogSheets.Count(); |
michael@0 | 4192 | } |
michael@0 | 4193 | |
michael@0 | 4194 | nsIStyleSheet* |
michael@0 | 4195 | nsDocument::GetCatalogStyleSheetAt(int32_t aIndex) const |
michael@0 | 4196 | { |
michael@0 | 4197 | NS_ENSURE_TRUE(0 <= aIndex && aIndex < mCatalogSheets.Count(), nullptr); |
michael@0 | 4198 | return mCatalogSheets[aIndex]; |
michael@0 | 4199 | } |
michael@0 | 4200 | |
michael@0 | 4201 | void |
michael@0 | 4202 | nsDocument::AddCatalogStyleSheet(nsCSSStyleSheet* aSheet) |
michael@0 | 4203 | { |
michael@0 | 4204 | mCatalogSheets.AppendObject(aSheet); |
michael@0 | 4205 | aSheet->SetOwningDocument(this); |
michael@0 | 4206 | aSheet->SetOwningNode(this); |
michael@0 | 4207 | |
michael@0 | 4208 | if (aSheet->IsApplicable()) { |
michael@0 | 4209 | // This is like |AddStyleSheetToStyleSets|, but for an agent sheet. |
michael@0 | 4210 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 4211 | if (shell) { |
michael@0 | 4212 | shell->StyleSet()->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet); |
michael@0 | 4213 | } |
michael@0 | 4214 | } |
michael@0 | 4215 | |
michael@0 | 4216 | NotifyStyleSheetAdded(aSheet, false); |
michael@0 | 4217 | } |
michael@0 | 4218 | |
michael@0 | 4219 | void |
michael@0 | 4220 | nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI) |
michael@0 | 4221 | { |
michael@0 | 4222 | mozilla::css::Loader* cssLoader = CSSLoader(); |
michael@0 | 4223 | if (cssLoader->GetEnabled()) { |
michael@0 | 4224 | int32_t sheetCount = GetNumberOfCatalogStyleSheets(); |
michael@0 | 4225 | for (int32_t i = 0; i < sheetCount; i++) { |
michael@0 | 4226 | nsIStyleSheet* sheet = GetCatalogStyleSheetAt(i); |
michael@0 | 4227 | NS_ASSERTION(sheet, "unexpected null stylesheet in the document"); |
michael@0 | 4228 | if (sheet) { |
michael@0 | 4229 | nsAutoCString uriStr; |
michael@0 | 4230 | sheet->GetSheetURI()->GetSpec(uriStr); |
michael@0 | 4231 | if (uriStr.Equals(aStyleSheetURI)) |
michael@0 | 4232 | return; |
michael@0 | 4233 | } |
michael@0 | 4234 | } |
michael@0 | 4235 | |
michael@0 | 4236 | nsCOMPtr<nsIURI> uri; |
michael@0 | 4237 | NS_NewURI(getter_AddRefs(uri), aStyleSheetURI); |
michael@0 | 4238 | if (uri) { |
michael@0 | 4239 | nsRefPtr<nsCSSStyleSheet> sheet; |
michael@0 | 4240 | cssLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet)); |
michael@0 | 4241 | if (sheet) { |
michael@0 | 4242 | BeginUpdate(UPDATE_STYLE); |
michael@0 | 4243 | AddCatalogStyleSheet(sheet); |
michael@0 | 4244 | EndUpdate(UPDATE_STYLE); |
michael@0 | 4245 | } |
michael@0 | 4246 | } |
michael@0 | 4247 | } |
michael@0 | 4248 | } |
michael@0 | 4249 | |
michael@0 | 4250 | static nsStyleSet::sheetType |
michael@0 | 4251 | ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType) |
michael@0 | 4252 | { |
michael@0 | 4253 | switch(aType) { |
michael@0 | 4254 | case nsIDocument::eAgentSheet: |
michael@0 | 4255 | return nsStyleSet::eAgentSheet; |
michael@0 | 4256 | case nsIDocument::eUserSheet: |
michael@0 | 4257 | return nsStyleSet::eUserSheet; |
michael@0 | 4258 | case nsIDocument::eAuthorSheet: |
michael@0 | 4259 | return nsStyleSet::eDocSheet; |
michael@0 | 4260 | default: |
michael@0 | 4261 | NS_ASSERTION(false, "wrong type"); |
michael@0 | 4262 | // we must return something although this should never happen |
michael@0 | 4263 | return nsStyleSet::eSheetTypeCount; |
michael@0 | 4264 | } |
michael@0 | 4265 | } |
michael@0 | 4266 | |
michael@0 | 4267 | static int32_t |
michael@0 | 4268 | FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI) |
michael@0 | 4269 | { |
michael@0 | 4270 | for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) { |
michael@0 | 4271 | bool bEqual; |
michael@0 | 4272 | nsIURI* uri = aSheets[i]->GetSheetURI(); |
michael@0 | 4273 | |
michael@0 | 4274 | if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual) |
michael@0 | 4275 | return i; |
michael@0 | 4276 | } |
michael@0 | 4277 | |
michael@0 | 4278 | return -1; |
michael@0 | 4279 | } |
michael@0 | 4280 | |
michael@0 | 4281 | nsresult |
michael@0 | 4282 | nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) |
michael@0 | 4283 | { |
michael@0 | 4284 | NS_PRECONDITION(aSheetURI, "null arg"); |
michael@0 | 4285 | |
michael@0 | 4286 | // Checking if we have loaded this one already. |
michael@0 | 4287 | if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0) |
michael@0 | 4288 | return NS_ERROR_INVALID_ARG; |
michael@0 | 4289 | |
michael@0 | 4290 | // Loading the sheet sync. |
michael@0 | 4291 | nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader(); |
michael@0 | 4292 | |
michael@0 | 4293 | nsRefPtr<nsCSSStyleSheet> sheet; |
michael@0 | 4294 | nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet, |
michael@0 | 4295 | true, getter_AddRefs(sheet)); |
michael@0 | 4296 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 4297 | |
michael@0 | 4298 | mAdditionalSheets[aType].AppendObject(sheet); |
michael@0 | 4299 | sheet->SetOwningDocument(this); |
michael@0 | 4300 | MOZ_ASSERT(sheet->IsApplicable()); |
michael@0 | 4301 | |
michael@0 | 4302 | BeginUpdate(UPDATE_STYLE); |
michael@0 | 4303 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 4304 | if (shell) { |
michael@0 | 4305 | nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); |
michael@0 | 4306 | shell->StyleSet()->AppendStyleSheet(type, sheet); |
michael@0 | 4307 | } |
michael@0 | 4308 | |
michael@0 | 4309 | // Passing false, so documet.styleSheets.length will not be affected by |
michael@0 | 4310 | // these additional sheets. |
michael@0 | 4311 | NotifyStyleSheetAdded(sheet, false); |
michael@0 | 4312 | EndUpdate(UPDATE_STYLE); |
michael@0 | 4313 | |
michael@0 | 4314 | return NS_OK; |
michael@0 | 4315 | } |
michael@0 | 4316 | |
michael@0 | 4317 | void |
michael@0 | 4318 | nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) |
michael@0 | 4319 | { |
michael@0 | 4320 | MOZ_ASSERT(aSheetURI); |
michael@0 | 4321 | |
michael@0 | 4322 | nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType]; |
michael@0 | 4323 | |
michael@0 | 4324 | int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI); |
michael@0 | 4325 | if (i >= 0) { |
michael@0 | 4326 | nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i]; |
michael@0 | 4327 | sheets.RemoveObjectAt(i); |
michael@0 | 4328 | |
michael@0 | 4329 | BeginUpdate(UPDATE_STYLE); |
michael@0 | 4330 | if (!mIsGoingAway) { |
michael@0 | 4331 | MOZ_ASSERT(sheetRef->IsApplicable()); |
michael@0 | 4332 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 4333 | if (shell) { |
michael@0 | 4334 | nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); |
michael@0 | 4335 | shell->StyleSet()->RemoveStyleSheet(type, sheetRef); |
michael@0 | 4336 | } |
michael@0 | 4337 | } |
michael@0 | 4338 | |
michael@0 | 4339 | // Passing false, so documet.styleSheets.length will not be affected by |
michael@0 | 4340 | // these additional sheets. |
michael@0 | 4341 | NotifyStyleSheetRemoved(sheetRef, false); |
michael@0 | 4342 | EndUpdate(UPDATE_STYLE); |
michael@0 | 4343 | |
michael@0 | 4344 | sheetRef->SetOwningDocument(nullptr); |
michael@0 | 4345 | } |
michael@0 | 4346 | } |
michael@0 | 4347 | |
michael@0 | 4348 | nsIStyleSheet* |
michael@0 | 4349 | nsDocument::FirstAdditionalAuthorSheet() |
michael@0 | 4350 | { |
michael@0 | 4351 | return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0); |
michael@0 | 4352 | } |
michael@0 | 4353 | |
michael@0 | 4354 | nsIGlobalObject* |
michael@0 | 4355 | nsDocument::GetScopeObject() const |
michael@0 | 4356 | { |
michael@0 | 4357 | nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject)); |
michael@0 | 4358 | return scope; |
michael@0 | 4359 | } |
michael@0 | 4360 | |
michael@0 | 4361 | void |
michael@0 | 4362 | nsDocument::SetScopeObject(nsIGlobalObject* aGlobal) |
michael@0 | 4363 | { |
michael@0 | 4364 | mScopeObject = do_GetWeakReference(aGlobal); |
michael@0 | 4365 | if (aGlobal) { |
michael@0 | 4366 | mHasHadScriptHandlingObject = true; |
michael@0 | 4367 | } |
michael@0 | 4368 | } |
michael@0 | 4369 | |
michael@0 | 4370 | static void |
michael@0 | 4371 | NotifyActivityChanged(nsIContent *aContent, void *aUnused) |
michael@0 | 4372 | { |
michael@0 | 4373 | nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent)); |
michael@0 | 4374 | if (domMediaElem) { |
michael@0 | 4375 | HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(aContent); |
michael@0 | 4376 | mediaElem->NotifyOwnerDocumentActivityChanged(); |
michael@0 | 4377 | } |
michael@0 | 4378 | nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aContent)); |
michael@0 | 4379 | if (objectLoadingContent) { |
michael@0 | 4380 | nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get()); |
michael@0 | 4381 | olc->NotifyOwnerDocumentActivityChanged(); |
michael@0 | 4382 | } |
michael@0 | 4383 | } |
michael@0 | 4384 | |
michael@0 | 4385 | void |
michael@0 | 4386 | nsIDocument::SetContainer(nsDocShell* aContainer) |
michael@0 | 4387 | { |
michael@0 | 4388 | if (aContainer) { |
michael@0 | 4389 | mDocumentContainer = aContainer->asWeakPtr(); |
michael@0 | 4390 | } else { |
michael@0 | 4391 | mDocumentContainer = WeakPtr<nsDocShell>(); |
michael@0 | 4392 | } |
michael@0 | 4393 | |
michael@0 | 4394 | EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
michael@0 | 4395 | if (!aContainer) { |
michael@0 | 4396 | return; |
michael@0 | 4397 | } |
michael@0 | 4398 | |
michael@0 | 4399 | // Get the Docshell |
michael@0 | 4400 | if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) { |
michael@0 | 4401 | // check if same type root |
michael@0 | 4402 | nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; |
michael@0 | 4403 | aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); |
michael@0 | 4404 | NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); |
michael@0 | 4405 | |
michael@0 | 4406 | if (sameTypeRoot == aContainer) { |
michael@0 | 4407 | static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true); |
michael@0 | 4408 | } |
michael@0 | 4409 | } |
michael@0 | 4410 | } |
michael@0 | 4411 | |
michael@0 | 4412 | nsISupports* |
michael@0 | 4413 | nsIDocument::GetContainer() const |
michael@0 | 4414 | { |
michael@0 | 4415 | return static_cast<nsIDocShell*>(mDocumentContainer); |
michael@0 | 4416 | } |
michael@0 | 4417 | |
michael@0 | 4418 | void |
michael@0 | 4419 | nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) |
michael@0 | 4420 | { |
michael@0 | 4421 | #ifdef DEBUG |
michael@0 | 4422 | { |
michael@0 | 4423 | nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject)); |
michael@0 | 4424 | |
michael@0 | 4425 | NS_ASSERTION(!win || win->IsInnerWindow(), |
michael@0 | 4426 | "Script global object must be an inner window!"); |
michael@0 | 4427 | } |
michael@0 | 4428 | #endif |
michael@0 | 4429 | NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController || |
michael@0 | 4430 | mAnimationController->IsPausedByType( |
michael@0 | 4431 | nsSMILTimeContainer::PAUSE_PAGEHIDE | |
michael@0 | 4432 | nsSMILTimeContainer::PAUSE_BEGIN), |
michael@0 | 4433 | "Clearing window pointer while animations are unpaused"); |
michael@0 | 4434 | |
michael@0 | 4435 | if (mScriptGlobalObject && !aScriptGlobalObject) { |
michael@0 | 4436 | // We're detaching from the window. We need to grab a pointer to |
michael@0 | 4437 | // our layout history state now. |
michael@0 | 4438 | mLayoutHistoryState = GetLayoutHistoryState(); |
michael@0 | 4439 | |
michael@0 | 4440 | if (mPresShell && !EventHandlingSuppressed()) { |
michael@0 | 4441 | RevokeAnimationFrameNotifications(); |
michael@0 | 4442 | } |
michael@0 | 4443 | |
michael@0 | 4444 | // Also make sure to remove our onload blocker now if we haven't done it yet |
michael@0 | 4445 | if (mOnloadBlockCount != 0) { |
michael@0 | 4446 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
michael@0 | 4447 | if (loadGroup) { |
michael@0 | 4448 | loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); |
michael@0 | 4449 | } |
michael@0 | 4450 | } |
michael@0 | 4451 | } |
michael@0 | 4452 | |
michael@0 | 4453 | mScriptGlobalObject = aScriptGlobalObject; |
michael@0 | 4454 | |
michael@0 | 4455 | if (aScriptGlobalObject) { |
michael@0 | 4456 | mHasHadScriptHandlingObject = true; |
michael@0 | 4457 | mHasHadDefaultView = true; |
michael@0 | 4458 | // Go back to using the docshell for the layout history state |
michael@0 | 4459 | mLayoutHistoryState = nullptr; |
michael@0 | 4460 | mScopeObject = do_GetWeakReference(aScriptGlobalObject); |
michael@0 | 4461 | #ifdef DEBUG |
michael@0 | 4462 | if (!mWillReparent) { |
michael@0 | 4463 | // We really shouldn't have a wrapper here but if we do we need to make sure |
michael@0 | 4464 | // it has the correct parent. |
michael@0 | 4465 | JSObject *obj = GetWrapperPreserveColor(); |
michael@0 | 4466 | if (obj) { |
michael@0 | 4467 | JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject(); |
michael@0 | 4468 | NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope, |
michael@0 | 4469 | "Wrong scope, this is really bad!"); |
michael@0 | 4470 | } |
michael@0 | 4471 | } |
michael@0 | 4472 | #endif |
michael@0 | 4473 | |
michael@0 | 4474 | if (mAllowDNSPrefetch) { |
michael@0 | 4475 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 4476 | if (docShell) { |
michael@0 | 4477 | #ifdef DEBUG |
michael@0 | 4478 | nsCOMPtr<nsIWebNavigation> webNav = |
michael@0 | 4479 | do_GetInterface(aScriptGlobalObject); |
michael@0 | 4480 | NS_ASSERTION(SameCOMIdentity(webNav, docShell), |
michael@0 | 4481 | "Unexpected container or script global?"); |
michael@0 | 4482 | #endif |
michael@0 | 4483 | bool allowDNSPrefetch; |
michael@0 | 4484 | docShell->GetAllowDNSPrefetch(&allowDNSPrefetch); |
michael@0 | 4485 | mAllowDNSPrefetch = allowDNSPrefetch; |
michael@0 | 4486 | } |
michael@0 | 4487 | } |
michael@0 | 4488 | |
michael@0 | 4489 | MaybeRescheduleAnimationFrameNotifications(); |
michael@0 | 4490 | mRegistry = new Registry(); |
michael@0 | 4491 | } |
michael@0 | 4492 | |
michael@0 | 4493 | // Remember the pointer to our window (or lack there of), to avoid |
michael@0 | 4494 | // having to QI every time it's asked for. |
michael@0 | 4495 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject); |
michael@0 | 4496 | mWindow = window; |
michael@0 | 4497 | |
michael@0 | 4498 | // Now that we know what our window is, we can flush the CSP errors to the |
michael@0 | 4499 | // Web Console. We are flushing all messages that occured and were stored |
michael@0 | 4500 | // in the queue prior to this point. |
michael@0 | 4501 | FlushCSPWebConsoleErrorQueue(); |
michael@0 | 4502 | nsCOMPtr<nsIHttpChannelInternal> internalChannel = |
michael@0 | 4503 | do_QueryInterface(GetChannel()); |
michael@0 | 4504 | if (internalChannel) { |
michael@0 | 4505 | nsCOMArray<nsISecurityConsoleMessage> messages; |
michael@0 | 4506 | internalChannel->TakeAllSecurityMessages(messages); |
michael@0 | 4507 | SendToConsole(messages); |
michael@0 | 4508 | } |
michael@0 | 4509 | |
michael@0 | 4510 | // Set our visibility state, but do not fire the event. This is correct |
michael@0 | 4511 | // because either we're coming out of bfcache (in which case IsVisible() will |
michael@0 | 4512 | // still test false at this point and no state change will happen) or we're |
michael@0 | 4513 | // doing the initial document load and don't want to fire the event for this |
michael@0 | 4514 | // change. |
michael@0 | 4515 | mVisibilityState = GetVisibilityState(); |
michael@0 | 4516 | } |
michael@0 | 4517 | |
michael@0 | 4518 | nsIScriptGlobalObject* |
michael@0 | 4519 | nsDocument::GetScriptHandlingObjectInternal() const |
michael@0 | 4520 | { |
michael@0 | 4521 | MOZ_ASSERT(!mScriptGlobalObject, |
michael@0 | 4522 | "Do not call this when mScriptGlobalObject is set!"); |
michael@0 | 4523 | if (mHasHadDefaultView) { |
michael@0 | 4524 | return nullptr; |
michael@0 | 4525 | } |
michael@0 | 4526 | |
michael@0 | 4527 | nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject = |
michael@0 | 4528 | do_QueryReferent(mScopeObject); |
michael@0 | 4529 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject); |
michael@0 | 4530 | if (win) { |
michael@0 | 4531 | NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!"); |
michael@0 | 4532 | nsPIDOMWindow* outer = win->GetOuterWindow(); |
michael@0 | 4533 | if (!outer || outer->GetCurrentInnerWindow() != win) { |
michael@0 | 4534 | NS_WARNING("Wrong inner/outer window combination!"); |
michael@0 | 4535 | return nullptr; |
michael@0 | 4536 | } |
michael@0 | 4537 | } |
michael@0 | 4538 | return scriptHandlingObject; |
michael@0 | 4539 | } |
michael@0 | 4540 | void |
michael@0 | 4541 | nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) |
michael@0 | 4542 | { |
michael@0 | 4543 | NS_ASSERTION(!mScriptGlobalObject || |
michael@0 | 4544 | mScriptGlobalObject == aScriptObject, |
michael@0 | 4545 | "Wrong script object!"); |
michael@0 | 4546 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject); |
michael@0 | 4547 | NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!"); |
michael@0 | 4548 | if (aScriptObject) { |
michael@0 | 4549 | mScopeObject = do_GetWeakReference(aScriptObject); |
michael@0 | 4550 | mHasHadScriptHandlingObject = true; |
michael@0 | 4551 | mHasHadDefaultView = false; |
michael@0 | 4552 | } |
michael@0 | 4553 | } |
michael@0 | 4554 | |
michael@0 | 4555 | bool |
michael@0 | 4556 | nsDocument::IsTopLevelContentDocument() |
michael@0 | 4557 | { |
michael@0 | 4558 | return mIsTopLevelContentDocument; |
michael@0 | 4559 | } |
michael@0 | 4560 | |
michael@0 | 4561 | void |
michael@0 | 4562 | nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument) |
michael@0 | 4563 | { |
michael@0 | 4564 | mIsTopLevelContentDocument = aIsTopLevelContentDocument; |
michael@0 | 4565 | } |
michael@0 | 4566 | |
michael@0 | 4567 | nsPIDOMWindow * |
michael@0 | 4568 | nsDocument::GetWindowInternal() const |
michael@0 | 4569 | { |
michael@0 | 4570 | MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!"); |
michael@0 | 4571 | // Let's use mScriptGlobalObject. Even if the document is already removed from |
michael@0 | 4572 | // the docshell, the outer window might be still obtainable from the it. |
michael@0 | 4573 | nsCOMPtr<nsPIDOMWindow> win; |
michael@0 | 4574 | if (mRemovedFromDocShell) { |
michael@0 | 4575 | nsCOMPtr<nsIInterfaceRequestor> requestor(mDocumentContainer); |
michael@0 | 4576 | if (requestor) { |
michael@0 | 4577 | // The docshell returns the outer window we are done. |
michael@0 | 4578 | win = do_GetInterface(requestor); |
michael@0 | 4579 | } |
michael@0 | 4580 | } else { |
michael@0 | 4581 | win = do_QueryInterface(mScriptGlobalObject); |
michael@0 | 4582 | if (win) { |
michael@0 | 4583 | // mScriptGlobalObject is always the inner window, let's get the outer. |
michael@0 | 4584 | win = win->GetOuterWindow(); |
michael@0 | 4585 | } |
michael@0 | 4586 | } |
michael@0 | 4587 | |
michael@0 | 4588 | return win; |
michael@0 | 4589 | } |
michael@0 | 4590 | |
michael@0 | 4591 | nsScriptLoader* |
michael@0 | 4592 | nsDocument::ScriptLoader() |
michael@0 | 4593 | { |
michael@0 | 4594 | return mScriptLoader; |
michael@0 | 4595 | } |
michael@0 | 4596 | |
michael@0 | 4597 | bool |
michael@0 | 4598 | nsDocument::InternalAllowXULXBL() |
michael@0 | 4599 | { |
michael@0 | 4600 | if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) { |
michael@0 | 4601 | mAllowXULXBL = eTriTrue; |
michael@0 | 4602 | return true; |
michael@0 | 4603 | } |
michael@0 | 4604 | |
michael@0 | 4605 | mAllowXULXBL = eTriFalse; |
michael@0 | 4606 | return false; |
michael@0 | 4607 | } |
michael@0 | 4608 | |
michael@0 | 4609 | // Note: We don't hold a reference to the document observer; we assume |
michael@0 | 4610 | // that it has a live reference to the document. |
michael@0 | 4611 | void |
michael@0 | 4612 | nsDocument::AddObserver(nsIDocumentObserver* aObserver) |
michael@0 | 4613 | { |
michael@0 | 4614 | NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex, |
michael@0 | 4615 | "Observer already in the list"); |
michael@0 | 4616 | mObservers.AppendElement(aObserver); |
michael@0 | 4617 | AddMutationObserver(aObserver); |
michael@0 | 4618 | } |
michael@0 | 4619 | |
michael@0 | 4620 | bool |
michael@0 | 4621 | nsDocument::RemoveObserver(nsIDocumentObserver* aObserver) |
michael@0 | 4622 | { |
michael@0 | 4623 | // If we're in the process of destroying the document (and we're |
michael@0 | 4624 | // informing the observers of the destruction), don't remove the |
michael@0 | 4625 | // observers from the list. This is not a big deal, since we |
michael@0 | 4626 | // don't hold a live reference to the observers. |
michael@0 | 4627 | if (!mInDestructor) { |
michael@0 | 4628 | RemoveMutationObserver(aObserver); |
michael@0 | 4629 | return mObservers.RemoveElement(aObserver); |
michael@0 | 4630 | } |
michael@0 | 4631 | |
michael@0 | 4632 | return mObservers.Contains(aObserver); |
michael@0 | 4633 | } |
michael@0 | 4634 | |
michael@0 | 4635 | void |
michael@0 | 4636 | nsDocument::MaybeEndOutermostXBLUpdate() |
michael@0 | 4637 | { |
michael@0 | 4638 | // Only call BindingManager()->EndOutermostUpdate() when |
michael@0 | 4639 | // we're not in an update and it is safe to run scripts. |
michael@0 | 4640 | if (mUpdateNestLevel == 0 && mInXBLUpdate) { |
michael@0 | 4641 | if (nsContentUtils::IsSafeToRunScript()) { |
michael@0 | 4642 | mInXBLUpdate = false; |
michael@0 | 4643 | BindingManager()->EndOutermostUpdate(); |
michael@0 | 4644 | } else if (!mInDestructor) { |
michael@0 | 4645 | nsContentUtils::AddScriptRunner( |
michael@0 | 4646 | NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate)); |
michael@0 | 4647 | } |
michael@0 | 4648 | } |
michael@0 | 4649 | } |
michael@0 | 4650 | |
michael@0 | 4651 | void |
michael@0 | 4652 | nsDocument::BeginUpdate(nsUpdateType aUpdateType) |
michael@0 | 4653 | { |
michael@0 | 4654 | if (mUpdateNestLevel == 0 && !mInXBLUpdate) { |
michael@0 | 4655 | mInXBLUpdate = true; |
michael@0 | 4656 | BindingManager()->BeginOutermostUpdate(); |
michael@0 | 4657 | } |
michael@0 | 4658 | |
michael@0 | 4659 | ++mUpdateNestLevel; |
michael@0 | 4660 | nsContentUtils::AddScriptBlocker(); |
michael@0 | 4661 | NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType)); |
michael@0 | 4662 | } |
michael@0 | 4663 | |
michael@0 | 4664 | void |
michael@0 | 4665 | nsDocument::EndUpdate(nsUpdateType aUpdateType) |
michael@0 | 4666 | { |
michael@0 | 4667 | NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType)); |
michael@0 | 4668 | |
michael@0 | 4669 | nsContentUtils::RemoveScriptBlocker(); |
michael@0 | 4670 | |
michael@0 | 4671 | --mUpdateNestLevel; |
michael@0 | 4672 | |
michael@0 | 4673 | // This set of updates may have created XBL bindings. Let the |
michael@0 | 4674 | // binding manager know we're done. |
michael@0 | 4675 | MaybeEndOutermostXBLUpdate(); |
michael@0 | 4676 | |
michael@0 | 4677 | MaybeInitializeFinalizeFrameLoaders(); |
michael@0 | 4678 | } |
michael@0 | 4679 | |
michael@0 | 4680 | void |
michael@0 | 4681 | nsDocument::BeginLoad() |
michael@0 | 4682 | { |
michael@0 | 4683 | // Block onload here to prevent having to deal with blocking and |
michael@0 | 4684 | // unblocking it while we know the document is loading. |
michael@0 | 4685 | BlockOnload(); |
michael@0 | 4686 | mDidFireDOMContentLoaded = false; |
michael@0 | 4687 | BlockDOMContentLoaded(); |
michael@0 | 4688 | |
michael@0 | 4689 | if (mScriptLoader) { |
michael@0 | 4690 | mScriptLoader->BeginDeferringScripts(); |
michael@0 | 4691 | } |
michael@0 | 4692 | |
michael@0 | 4693 | NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this)); |
michael@0 | 4694 | } |
michael@0 | 4695 | |
michael@0 | 4696 | void |
michael@0 | 4697 | nsDocument::ReportEmptyGetElementByIdArg() |
michael@0 | 4698 | { |
michael@0 | 4699 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 4700 | NS_LITERAL_CSTRING("DOM"), this, |
michael@0 | 4701 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 4702 | "EmptyGetElementByIdParam"); |
michael@0 | 4703 | } |
michael@0 | 4704 | |
michael@0 | 4705 | Element* |
michael@0 | 4706 | nsDocument::GetElementById(const nsAString& aElementId) |
michael@0 | 4707 | { |
michael@0 | 4708 | if (!CheckGetElementByIdArg(aElementId)) { |
michael@0 | 4709 | return nullptr; |
michael@0 | 4710 | } |
michael@0 | 4711 | |
michael@0 | 4712 | nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); |
michael@0 | 4713 | return entry ? entry->GetIdElement() : nullptr; |
michael@0 | 4714 | } |
michael@0 | 4715 | |
michael@0 | 4716 | const nsSmallVoidArray* |
michael@0 | 4717 | nsDocument::GetAllElementsForId(const nsAString& aElementId) const |
michael@0 | 4718 | { |
michael@0 | 4719 | if (aElementId.IsEmpty()) { |
michael@0 | 4720 | return nullptr; |
michael@0 | 4721 | } |
michael@0 | 4722 | |
michael@0 | 4723 | nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); |
michael@0 | 4724 | return entry ? entry->GetIdElements() : nullptr; |
michael@0 | 4725 | } |
michael@0 | 4726 | |
michael@0 | 4727 | NS_IMETHODIMP |
michael@0 | 4728 | nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn) |
michael@0 | 4729 | { |
michael@0 | 4730 | Element *content = GetElementById(aId); |
michael@0 | 4731 | if (content) { |
michael@0 | 4732 | return CallQueryInterface(content, aReturn); |
michael@0 | 4733 | } |
michael@0 | 4734 | |
michael@0 | 4735 | *aReturn = nullptr; |
michael@0 | 4736 | |
michael@0 | 4737 | return NS_OK; |
michael@0 | 4738 | } |
michael@0 | 4739 | |
michael@0 | 4740 | Element* |
michael@0 | 4741 | nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, |
michael@0 | 4742 | void* aData, bool aForImage) |
michael@0 | 4743 | { |
michael@0 | 4744 | nsDependentAtomString id(aID); |
michael@0 | 4745 | |
michael@0 | 4746 | if (!CheckGetElementByIdArg(id)) |
michael@0 | 4747 | return nullptr; |
michael@0 | 4748 | |
michael@0 | 4749 | nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id); |
michael@0 | 4750 | NS_ENSURE_TRUE(entry, nullptr); |
michael@0 | 4751 | |
michael@0 | 4752 | entry->AddContentChangeCallback(aObserver, aData, aForImage); |
michael@0 | 4753 | return aForImage ? entry->GetImageIdElement() : entry->GetIdElement(); |
michael@0 | 4754 | } |
michael@0 | 4755 | |
michael@0 | 4756 | void |
michael@0 | 4757 | nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, |
michael@0 | 4758 | void* aData, bool aForImage) |
michael@0 | 4759 | { |
michael@0 | 4760 | nsDependentAtomString id(aID); |
michael@0 | 4761 | |
michael@0 | 4762 | if (!CheckGetElementByIdArg(id)) |
michael@0 | 4763 | return; |
michael@0 | 4764 | |
michael@0 | 4765 | nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id); |
michael@0 | 4766 | if (!entry) { |
michael@0 | 4767 | return; |
michael@0 | 4768 | } |
michael@0 | 4769 | |
michael@0 | 4770 | entry->RemoveContentChangeCallback(aObserver, aData, aForImage); |
michael@0 | 4771 | } |
michael@0 | 4772 | |
michael@0 | 4773 | NS_IMETHODIMP |
michael@0 | 4774 | nsDocument::MozSetImageElement(const nsAString& aImageElementId, |
michael@0 | 4775 | nsIDOMElement* aImageElement) |
michael@0 | 4776 | { |
michael@0 | 4777 | nsCOMPtr<Element> el = do_QueryInterface(aImageElement); |
michael@0 | 4778 | MozSetImageElement(aImageElementId, el); |
michael@0 | 4779 | return NS_OK; |
michael@0 | 4780 | } |
michael@0 | 4781 | |
michael@0 | 4782 | void |
michael@0 | 4783 | nsDocument::MozSetImageElement(const nsAString& aImageElementId, |
michael@0 | 4784 | Element* aElement) |
michael@0 | 4785 | { |
michael@0 | 4786 | if (aImageElementId.IsEmpty()) |
michael@0 | 4787 | return; |
michael@0 | 4788 | |
michael@0 | 4789 | // Hold a script blocker while calling SetImageElement since that can call |
michael@0 | 4790 | // out to id-observers |
michael@0 | 4791 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 4792 | |
michael@0 | 4793 | nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId); |
michael@0 | 4794 | if (entry) { |
michael@0 | 4795 | entry->SetImageElement(aElement); |
michael@0 | 4796 | if (entry->IsEmpty()) { |
michael@0 | 4797 | mIdentifierMap.RemoveEntry(aImageElementId); |
michael@0 | 4798 | } |
michael@0 | 4799 | } |
michael@0 | 4800 | } |
michael@0 | 4801 | |
michael@0 | 4802 | Element* |
michael@0 | 4803 | nsDocument::LookupImageElement(const nsAString& aId) |
michael@0 | 4804 | { |
michael@0 | 4805 | if (aId.IsEmpty()) |
michael@0 | 4806 | return nullptr; |
michael@0 | 4807 | |
michael@0 | 4808 | nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); |
michael@0 | 4809 | return entry ? entry->GetImageIdElement() : nullptr; |
michael@0 | 4810 | } |
michael@0 | 4811 | |
michael@0 | 4812 | void |
michael@0 | 4813 | nsDocument::DispatchContentLoadedEvents() |
michael@0 | 4814 | { |
michael@0 | 4815 | // If you add early returns from this method, make sure you're |
michael@0 | 4816 | // calling UnblockOnload properly. |
michael@0 | 4817 | |
michael@0 | 4818 | // Unpin references to preloaded images |
michael@0 | 4819 | mPreloadingImages.Clear(); |
michael@0 | 4820 | |
michael@0 | 4821 | if (mTiming) { |
michael@0 | 4822 | mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI()); |
michael@0 | 4823 | } |
michael@0 | 4824 | |
michael@0 | 4825 | // Dispatch observer notification to notify observers document is interactive. |
michael@0 | 4826 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 4827 | nsIPrincipal *principal = GetPrincipal(); |
michael@0 | 4828 | os->NotifyObservers(static_cast<nsIDocument*>(this), |
michael@0 | 4829 | nsContentUtils::IsSystemPrincipal(principal) ? |
michael@0 | 4830 | "chrome-document-interactive" : |
michael@0 | 4831 | "content-document-interactive", |
michael@0 | 4832 | nullptr); |
michael@0 | 4833 | |
michael@0 | 4834 | // Fire a DOM event notifying listeners that this document has been |
michael@0 | 4835 | // loaded (excluding images and other loads initiated by this |
michael@0 | 4836 | // document). |
michael@0 | 4837 | nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
michael@0 | 4838 | NS_LITERAL_STRING("DOMContentLoaded"), |
michael@0 | 4839 | true, true); |
michael@0 | 4840 | |
michael@0 | 4841 | if (mTiming) { |
michael@0 | 4842 | mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI()); |
michael@0 | 4843 | } |
michael@0 | 4844 | |
michael@0 | 4845 | // If this document is a [i]frame, fire a DOMFrameContentLoaded |
michael@0 | 4846 | // event on all parent documents notifying that the HTML (excluding |
michael@0 | 4847 | // other external files such as images and stylesheets) in a frame |
michael@0 | 4848 | // has finished loading. |
michael@0 | 4849 | |
michael@0 | 4850 | // target_frame is the [i]frame element that will be used as the |
michael@0 | 4851 | // target for the event. It's the [i]frame whose content is done |
michael@0 | 4852 | // loading. |
michael@0 | 4853 | nsCOMPtr<EventTarget> target_frame; |
michael@0 | 4854 | |
michael@0 | 4855 | if (mParentDocument) { |
michael@0 | 4856 | target_frame = mParentDocument->FindContentForSubDocument(this); |
michael@0 | 4857 | } |
michael@0 | 4858 | |
michael@0 | 4859 | if (target_frame) { |
michael@0 | 4860 | nsCOMPtr<nsIDocument> parent = mParentDocument; |
michael@0 | 4861 | do { |
michael@0 | 4862 | nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent); |
michael@0 | 4863 | |
michael@0 | 4864 | nsCOMPtr<nsIDOMEvent> event; |
michael@0 | 4865 | if (domDoc) { |
michael@0 | 4866 | domDoc->CreateEvent(NS_LITERAL_STRING("Events"), |
michael@0 | 4867 | getter_AddRefs(event)); |
michael@0 | 4868 | |
michael@0 | 4869 | } |
michael@0 | 4870 | |
michael@0 | 4871 | if (event) { |
michael@0 | 4872 | event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true, |
michael@0 | 4873 | true); |
michael@0 | 4874 | |
michael@0 | 4875 | event->SetTarget(target_frame); |
michael@0 | 4876 | event->SetTrusted(true); |
michael@0 | 4877 | |
michael@0 | 4878 | // To dispatch this event we must manually call |
michael@0 | 4879 | // EventDispatcher::Dispatch() on the ancestor document since the |
michael@0 | 4880 | // target is not in the same document, so the event would never reach |
michael@0 | 4881 | // the ancestor document if we used the normal event |
michael@0 | 4882 | // dispatching code. |
michael@0 | 4883 | |
michael@0 | 4884 | WidgetEvent* innerEvent = event->GetInternalNSEvent(); |
michael@0 | 4885 | if (innerEvent) { |
michael@0 | 4886 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 4887 | |
michael@0 | 4888 | nsIPresShell *shell = parent->GetShell(); |
michael@0 | 4889 | if (shell) { |
michael@0 | 4890 | nsRefPtr<nsPresContext> context = shell->GetPresContext(); |
michael@0 | 4891 | |
michael@0 | 4892 | if (context) { |
michael@0 | 4893 | EventDispatcher::Dispatch(parent, context, innerEvent, event, |
michael@0 | 4894 | &status); |
michael@0 | 4895 | } |
michael@0 | 4896 | } |
michael@0 | 4897 | } |
michael@0 | 4898 | } |
michael@0 | 4899 | |
michael@0 | 4900 | parent = parent->GetParentDocument(); |
michael@0 | 4901 | } while (parent); |
michael@0 | 4902 | } |
michael@0 | 4903 | |
michael@0 | 4904 | // If the document has a manifest attribute, fire a MozApplicationManifest |
michael@0 | 4905 | // event. |
michael@0 | 4906 | Element* root = GetRootElement(); |
michael@0 | 4907 | if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) { |
michael@0 | 4908 | nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this), |
michael@0 | 4909 | NS_LITERAL_STRING("MozApplicationManifest"), |
michael@0 | 4910 | true, true); |
michael@0 | 4911 | } |
michael@0 | 4912 | |
michael@0 | 4913 | UnblockOnload(true); |
michael@0 | 4914 | } |
michael@0 | 4915 | |
michael@0 | 4916 | void |
michael@0 | 4917 | nsDocument::EndLoad() |
michael@0 | 4918 | { |
michael@0 | 4919 | // Drop the ref to our parser, if any, but keep hold of the sink so that we |
michael@0 | 4920 | // can flush it from FlushPendingNotifications as needed. We might have to |
michael@0 | 4921 | // do that to get a StartLayout() to happen. |
michael@0 | 4922 | if (mParser) { |
michael@0 | 4923 | mWeakSink = do_GetWeakReference(mParser->GetContentSink()); |
michael@0 | 4924 | mParser = nullptr; |
michael@0 | 4925 | } |
michael@0 | 4926 | |
michael@0 | 4927 | NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); |
michael@0 | 4928 | |
michael@0 | 4929 | UnblockDOMContentLoaded(); |
michael@0 | 4930 | } |
michael@0 | 4931 | |
michael@0 | 4932 | void |
michael@0 | 4933 | nsDocument::UnblockDOMContentLoaded() |
michael@0 | 4934 | { |
michael@0 | 4935 | MOZ_ASSERT(mBlockDOMContentLoaded); |
michael@0 | 4936 | if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) { |
michael@0 | 4937 | return; |
michael@0 | 4938 | } |
michael@0 | 4939 | mDidFireDOMContentLoaded = true; |
michael@0 | 4940 | |
michael@0 | 4941 | MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); |
michael@0 | 4942 | if (!mSynchronousDOMContentLoaded) { |
michael@0 | 4943 | nsRefPtr<nsIRunnable> ev = |
michael@0 | 4944 | NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); |
michael@0 | 4945 | NS_DispatchToCurrentThread(ev); |
michael@0 | 4946 | } else { |
michael@0 | 4947 | DispatchContentLoadedEvents(); |
michael@0 | 4948 | } |
michael@0 | 4949 | } |
michael@0 | 4950 | |
michael@0 | 4951 | void |
michael@0 | 4952 | nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) |
michael@0 | 4953 | { |
michael@0 | 4954 | NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(), |
michael@0 | 4955 | "Someone forgot a scriptblocker"); |
michael@0 | 4956 | NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged, |
michael@0 | 4957 | (this, aContent, aStateMask)); |
michael@0 | 4958 | } |
michael@0 | 4959 | |
michael@0 | 4960 | void |
michael@0 | 4961 | nsDocument::DocumentStatesChanged(EventStates aStateMask) |
michael@0 | 4962 | { |
michael@0 | 4963 | // Invalidate our cached state. |
michael@0 | 4964 | mGotDocumentState &= ~aStateMask; |
michael@0 | 4965 | mDocumentState &= ~aStateMask; |
michael@0 | 4966 | |
michael@0 | 4967 | NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask)); |
michael@0 | 4968 | } |
michael@0 | 4969 | |
michael@0 | 4970 | void |
michael@0 | 4971 | nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet, |
michael@0 | 4972 | nsIStyleRule* aOldStyleRule, |
michael@0 | 4973 | nsIStyleRule* aNewStyleRule) |
michael@0 | 4974 | { |
michael@0 | 4975 | NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, |
michael@0 | 4976 | (this, aSheet, |
michael@0 | 4977 | aOldStyleRule, aNewStyleRule)); |
michael@0 | 4978 | |
michael@0 | 4979 | if (StyleSheetChangeEventsEnabled()) { |
michael@0 | 4980 | nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule); |
michael@0 | 4981 | DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent, |
michael@0 | 4982 | nsIDOMStyleRuleChangeEvent, |
michael@0 | 4983 | InitStyleRuleChangeEvent, |
michael@0 | 4984 | "StyleRuleChanged", |
michael@0 | 4985 | rule ? rule->GetDOMRule() : nullptr); |
michael@0 | 4986 | } |
michael@0 | 4987 | } |
michael@0 | 4988 | |
michael@0 | 4989 | void |
michael@0 | 4990 | nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet, |
michael@0 | 4991 | nsIStyleRule* aStyleRule) |
michael@0 | 4992 | { |
michael@0 | 4993 | NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, |
michael@0 | 4994 | (this, aSheet, aStyleRule)); |
michael@0 | 4995 | |
michael@0 | 4996 | if (StyleSheetChangeEventsEnabled()) { |
michael@0 | 4997 | nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule); |
michael@0 | 4998 | DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent, |
michael@0 | 4999 | nsIDOMStyleRuleChangeEvent, |
michael@0 | 5000 | InitStyleRuleChangeEvent, |
michael@0 | 5001 | "StyleRuleAdded", |
michael@0 | 5002 | rule ? rule->GetDOMRule() : nullptr); |
michael@0 | 5003 | } |
michael@0 | 5004 | } |
michael@0 | 5005 | |
michael@0 | 5006 | void |
michael@0 | 5007 | nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet, |
michael@0 | 5008 | nsIStyleRule* aStyleRule) |
michael@0 | 5009 | { |
michael@0 | 5010 | NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, |
michael@0 | 5011 | (this, aSheet, aStyleRule)); |
michael@0 | 5012 | |
michael@0 | 5013 | if (StyleSheetChangeEventsEnabled()) { |
michael@0 | 5014 | nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule); |
michael@0 | 5015 | DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent, |
michael@0 | 5016 | nsIDOMStyleRuleChangeEvent, |
michael@0 | 5017 | InitStyleRuleChangeEvent, |
michael@0 | 5018 | "StyleRuleRemoved", |
michael@0 | 5019 | rule ? rule->GetDOMRule() : nullptr); |
michael@0 | 5020 | } |
michael@0 | 5021 | } |
michael@0 | 5022 | |
michael@0 | 5023 | #undef DO_STYLESHEET_NOTIFICATION |
michael@0 | 5024 | |
michael@0 | 5025 | |
michael@0 | 5026 | // |
michael@0 | 5027 | // nsIDOMDocument interface |
michael@0 | 5028 | // |
michael@0 | 5029 | DocumentType* |
michael@0 | 5030 | nsIDocument::GetDoctype() const |
michael@0 | 5031 | { |
michael@0 | 5032 | for (nsIContent* child = GetFirstChild(); |
michael@0 | 5033 | child; |
michael@0 | 5034 | child = child->GetNextSibling()) { |
michael@0 | 5035 | if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { |
michael@0 | 5036 | return static_cast<DocumentType*>(child); |
michael@0 | 5037 | } |
michael@0 | 5038 | } |
michael@0 | 5039 | return nullptr; |
michael@0 | 5040 | } |
michael@0 | 5041 | |
michael@0 | 5042 | NS_IMETHODIMP |
michael@0 | 5043 | nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype) |
michael@0 | 5044 | { |
michael@0 | 5045 | MOZ_ASSERT(aDoctype); |
michael@0 | 5046 | nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype(); |
michael@0 | 5047 | doctype.forget(aDoctype); |
michael@0 | 5048 | return NS_OK; |
michael@0 | 5049 | } |
michael@0 | 5050 | |
michael@0 | 5051 | NS_IMETHODIMP |
michael@0 | 5052 | nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation) |
michael@0 | 5053 | { |
michael@0 | 5054 | ErrorResult rv; |
michael@0 | 5055 | *aImplementation = GetImplementation(rv); |
michael@0 | 5056 | if (rv.Failed()) { |
michael@0 | 5057 | MOZ_ASSERT(!*aImplementation); |
michael@0 | 5058 | return rv.ErrorCode(); |
michael@0 | 5059 | } |
michael@0 | 5060 | NS_ADDREF(*aImplementation); |
michael@0 | 5061 | return NS_OK; |
michael@0 | 5062 | } |
michael@0 | 5063 | |
michael@0 | 5064 | DOMImplementation* |
michael@0 | 5065 | nsDocument::GetImplementation(ErrorResult& rv) |
michael@0 | 5066 | { |
michael@0 | 5067 | if (!mDOMImplementation) { |
michael@0 | 5068 | nsCOMPtr<nsIURI> uri; |
michael@0 | 5069 | NS_NewURI(getter_AddRefs(uri), "about:blank"); |
michael@0 | 5070 | if (!uri) { |
michael@0 | 5071 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 5072 | return nullptr; |
michael@0 | 5073 | } |
michael@0 | 5074 | bool hasHadScriptObject = true; |
michael@0 | 5075 | nsIScriptGlobalObject* scriptObject = |
michael@0 | 5076 | GetScriptHandlingObject(hasHadScriptObject); |
michael@0 | 5077 | if (!scriptObject && hasHadScriptObject) { |
michael@0 | 5078 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5079 | return nullptr; |
michael@0 | 5080 | } |
michael@0 | 5081 | mDOMImplementation = new DOMImplementation(this, |
michael@0 | 5082 | scriptObject ? scriptObject : GetScopeObject(), uri, uri); |
michael@0 | 5083 | } |
michael@0 | 5084 | |
michael@0 | 5085 | return mDOMImplementation; |
michael@0 | 5086 | } |
michael@0 | 5087 | |
michael@0 | 5088 | NS_IMETHODIMP |
michael@0 | 5089 | nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement) |
michael@0 | 5090 | { |
michael@0 | 5091 | NS_ENSURE_ARG_POINTER(aDocumentElement); |
michael@0 | 5092 | |
michael@0 | 5093 | Element* root = GetRootElement(); |
michael@0 | 5094 | if (root) { |
michael@0 | 5095 | return CallQueryInterface(root, aDocumentElement); |
michael@0 | 5096 | } |
michael@0 | 5097 | |
michael@0 | 5098 | *aDocumentElement = nullptr; |
michael@0 | 5099 | |
michael@0 | 5100 | return NS_OK; |
michael@0 | 5101 | } |
michael@0 | 5102 | |
michael@0 | 5103 | NS_IMETHODIMP |
michael@0 | 5104 | nsDocument::CreateElement(const nsAString& aTagName, |
michael@0 | 5105 | nsIDOMElement** aReturn) |
michael@0 | 5106 | { |
michael@0 | 5107 | *aReturn = nullptr; |
michael@0 | 5108 | ErrorResult rv; |
michael@0 | 5109 | nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv); |
michael@0 | 5110 | NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode()); |
michael@0 | 5111 | return CallQueryInterface(element, aReturn); |
michael@0 | 5112 | } |
michael@0 | 5113 | |
michael@0 | 5114 | bool IsLowercaseASCII(const nsAString& aValue) |
michael@0 | 5115 | { |
michael@0 | 5116 | int32_t len = aValue.Length(); |
michael@0 | 5117 | for (int32_t i = 0; i < len; ++i) { |
michael@0 | 5118 | char16_t c = aValue[i]; |
michael@0 | 5119 | if (!(0x0061 <= (c) && ((c) <= 0x007a))) { |
michael@0 | 5120 | return false; |
michael@0 | 5121 | } |
michael@0 | 5122 | } |
michael@0 | 5123 | return true; |
michael@0 | 5124 | } |
michael@0 | 5125 | |
michael@0 | 5126 | already_AddRefed<Element> |
michael@0 | 5127 | nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv) |
michael@0 | 5128 | { |
michael@0 | 5129 | rv = nsContentUtils::CheckQName(aTagName, false); |
michael@0 | 5130 | if (rv.Failed()) { |
michael@0 | 5131 | return nullptr; |
michael@0 | 5132 | } |
michael@0 | 5133 | |
michael@0 | 5134 | bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName); |
michael@0 | 5135 | nsAutoString lcTagName; |
michael@0 | 5136 | if (needsLowercase) { |
michael@0 | 5137 | nsContentUtils::ASCIIToLower(aTagName, lcTagName); |
michael@0 | 5138 | } |
michael@0 | 5139 | |
michael@0 | 5140 | nsCOMPtr<nsIContent> content; |
michael@0 | 5141 | rv = CreateElem(needsLowercase ? lcTagName : aTagName, |
michael@0 | 5142 | nullptr, mDefaultElementType, getter_AddRefs(content)); |
michael@0 | 5143 | if (rv.Failed()) { |
michael@0 | 5144 | return nullptr; |
michael@0 | 5145 | } |
michael@0 | 5146 | return dont_AddRef(content.forget().take()->AsElement()); |
michael@0 | 5147 | } |
michael@0 | 5148 | |
michael@0 | 5149 | void |
michael@0 | 5150 | nsDocument::SwizzleCustomElement(Element* aElement, |
michael@0 | 5151 | const nsAString& aTypeExtension, |
michael@0 | 5152 | uint32_t aNamespaceID, |
michael@0 | 5153 | ErrorResult& rv) |
michael@0 | 5154 | { |
michael@0 | 5155 | nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension)); |
michael@0 | 5156 | nsCOMPtr<nsIAtom> tagAtom = aElement->Tag(); |
michael@0 | 5157 | if (!mRegistry || tagAtom == typeAtom) { |
michael@0 | 5158 | return; |
michael@0 | 5159 | } |
michael@0 | 5160 | |
michael@0 | 5161 | CustomElementDefinition* data; |
michael@0 | 5162 | CustomElementHashKey key(aNamespaceID, typeAtom); |
michael@0 | 5163 | if (!mRegistry->mCustomDefinitions.Get(&key, &data)) { |
michael@0 | 5164 | // The type extension doesn't exist in the registry, |
michael@0 | 5165 | // thus we don't need to swizzle, but it is possibly |
michael@0 | 5166 | // an upgrade candidate. |
michael@0 | 5167 | RegisterUnresolvedElement(aElement, typeAtom); |
michael@0 | 5168 | return; |
michael@0 | 5169 | } |
michael@0 | 5170 | |
michael@0 | 5171 | if (data->mLocalName != tagAtom) { |
michael@0 | 5172 | // The element doesn't match the local name for the |
michael@0 | 5173 | // definition, thus the element isn't a custom element |
michael@0 | 5174 | // and we don't need to do anything more. |
michael@0 | 5175 | return; |
michael@0 | 5176 | } |
michael@0 | 5177 | |
michael@0 | 5178 | if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) { |
michael@0 | 5179 | // Swizzling in the parser happens after the "is" attribute is added. |
michael@0 | 5180 | aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true); |
michael@0 | 5181 | } |
michael@0 | 5182 | |
michael@0 | 5183 | // Enqueuing the created callback will set the CustomElementData on the |
michael@0 | 5184 | // element, causing prototype swizzling to occur in Element::WrapObject. |
michael@0 | 5185 | EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data); |
michael@0 | 5186 | } |
michael@0 | 5187 | |
michael@0 | 5188 | already_AddRefed<Element> |
michael@0 | 5189 | nsDocument::CreateElement(const nsAString& aTagName, |
michael@0 | 5190 | const nsAString& aTypeExtension, |
michael@0 | 5191 | ErrorResult& rv) |
michael@0 | 5192 | { |
michael@0 | 5193 | nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv); |
michael@0 | 5194 | if (rv.Failed()) { |
michael@0 | 5195 | return nullptr; |
michael@0 | 5196 | } |
michael@0 | 5197 | |
michael@0 | 5198 | SwizzleCustomElement(elem, aTypeExtension, |
michael@0 | 5199 | GetDefaultNamespaceID(), rv); |
michael@0 | 5200 | if (rv.Failed()) { |
michael@0 | 5201 | return nullptr; |
michael@0 | 5202 | } |
michael@0 | 5203 | |
michael@0 | 5204 | return elem.forget(); |
michael@0 | 5205 | } |
michael@0 | 5206 | |
michael@0 | 5207 | NS_IMETHODIMP |
michael@0 | 5208 | nsDocument::CreateElementNS(const nsAString& aNamespaceURI, |
michael@0 | 5209 | const nsAString& aQualifiedName, |
michael@0 | 5210 | nsIDOMElement** aReturn) |
michael@0 | 5211 | { |
michael@0 | 5212 | *aReturn = nullptr; |
michael@0 | 5213 | ErrorResult rv; |
michael@0 | 5214 | nsCOMPtr<Element> element = |
michael@0 | 5215 | nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv); |
michael@0 | 5216 | NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode()); |
michael@0 | 5217 | return CallQueryInterface(element, aReturn); |
michael@0 | 5218 | } |
michael@0 | 5219 | |
michael@0 | 5220 | already_AddRefed<Element> |
michael@0 | 5221 | nsIDocument::CreateElementNS(const nsAString& aNamespaceURI, |
michael@0 | 5222 | const nsAString& aQualifiedName, |
michael@0 | 5223 | ErrorResult& rv) |
michael@0 | 5224 | { |
michael@0 | 5225 | nsCOMPtr<nsINodeInfo> nodeInfo; |
michael@0 | 5226 | rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, |
michael@0 | 5227 | aQualifiedName, |
michael@0 | 5228 | mNodeInfoManager, |
michael@0 | 5229 | nsIDOMNode::ELEMENT_NODE, |
michael@0 | 5230 | getter_AddRefs(nodeInfo)); |
michael@0 | 5231 | if (rv.Failed()) { |
michael@0 | 5232 | return nullptr; |
michael@0 | 5233 | } |
michael@0 | 5234 | |
michael@0 | 5235 | nsCOMPtr<Element> element; |
michael@0 | 5236 | rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), |
michael@0 | 5237 | NOT_FROM_PARSER); |
michael@0 | 5238 | if (rv.Failed()) { |
michael@0 | 5239 | return nullptr; |
michael@0 | 5240 | } |
michael@0 | 5241 | return element.forget(); |
michael@0 | 5242 | } |
michael@0 | 5243 | |
michael@0 | 5244 | already_AddRefed<Element> |
michael@0 | 5245 | nsDocument::CreateElementNS(const nsAString& aNamespaceURI, |
michael@0 | 5246 | const nsAString& aQualifiedName, |
michael@0 | 5247 | const nsAString& aTypeExtension, |
michael@0 | 5248 | ErrorResult& rv) |
michael@0 | 5249 | { |
michael@0 | 5250 | nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI, |
michael@0 | 5251 | aQualifiedName, |
michael@0 | 5252 | rv); |
michael@0 | 5253 | if (rv.Failed()) { |
michael@0 | 5254 | return nullptr; |
michael@0 | 5255 | } |
michael@0 | 5256 | |
michael@0 | 5257 | int32_t nameSpaceId = kNameSpaceID_Wildcard; |
michael@0 | 5258 | if (!aNamespaceURI.EqualsLiteral("*")) { |
michael@0 | 5259 | rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
michael@0 | 5260 | nameSpaceId); |
michael@0 | 5261 | if (rv.Failed()) { |
michael@0 | 5262 | return nullptr; |
michael@0 | 5263 | } |
michael@0 | 5264 | } |
michael@0 | 5265 | |
michael@0 | 5266 | SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv); |
michael@0 | 5267 | if (rv.Failed()) { |
michael@0 | 5268 | return nullptr; |
michael@0 | 5269 | } |
michael@0 | 5270 | |
michael@0 | 5271 | return elem.forget(); |
michael@0 | 5272 | } |
michael@0 | 5273 | |
michael@0 | 5274 | NS_IMETHODIMP |
michael@0 | 5275 | nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn) |
michael@0 | 5276 | { |
michael@0 | 5277 | *aReturn = nsIDocument::CreateTextNode(aData).take(); |
michael@0 | 5278 | return NS_OK; |
michael@0 | 5279 | } |
michael@0 | 5280 | |
michael@0 | 5281 | already_AddRefed<nsTextNode> |
michael@0 | 5282 | nsIDocument::CreateTextNode(const nsAString& aData) const |
michael@0 | 5283 | { |
michael@0 | 5284 | nsRefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager); |
michael@0 | 5285 | // Don't notify; this node is still being created. |
michael@0 | 5286 | text->SetText(aData, false); |
michael@0 | 5287 | return text.forget(); |
michael@0 | 5288 | } |
michael@0 | 5289 | |
michael@0 | 5290 | NS_IMETHODIMP |
michael@0 | 5291 | nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn) |
michael@0 | 5292 | { |
michael@0 | 5293 | *aReturn = nsIDocument::CreateDocumentFragment().take(); |
michael@0 | 5294 | return NS_OK; |
michael@0 | 5295 | } |
michael@0 | 5296 | |
michael@0 | 5297 | already_AddRefed<DocumentFragment> |
michael@0 | 5298 | nsIDocument::CreateDocumentFragment() const |
michael@0 | 5299 | { |
michael@0 | 5300 | nsRefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager); |
michael@0 | 5301 | return frag.forget(); |
michael@0 | 5302 | } |
michael@0 | 5303 | |
michael@0 | 5304 | NS_IMETHODIMP |
michael@0 | 5305 | nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn) |
michael@0 | 5306 | { |
michael@0 | 5307 | *aReturn = nsIDocument::CreateComment(aData).take(); |
michael@0 | 5308 | return NS_OK; |
michael@0 | 5309 | } |
michael@0 | 5310 | |
michael@0 | 5311 | // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers. |
michael@0 | 5312 | already_AddRefed<dom::Comment> |
michael@0 | 5313 | nsIDocument::CreateComment(const nsAString& aData) const |
michael@0 | 5314 | { |
michael@0 | 5315 | nsRefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager); |
michael@0 | 5316 | |
michael@0 | 5317 | // Don't notify; this node is still being created. |
michael@0 | 5318 | comment->SetText(aData, false); |
michael@0 | 5319 | return comment.forget(); |
michael@0 | 5320 | } |
michael@0 | 5321 | |
michael@0 | 5322 | NS_IMETHODIMP |
michael@0 | 5323 | nsDocument::CreateCDATASection(const nsAString& aData, |
michael@0 | 5324 | nsIDOMCDATASection** aReturn) |
michael@0 | 5325 | { |
michael@0 | 5326 | NS_ENSURE_ARG_POINTER(aReturn); |
michael@0 | 5327 | ErrorResult rv; |
michael@0 | 5328 | *aReturn = nsIDocument::CreateCDATASection(aData, rv).take(); |
michael@0 | 5329 | return rv.ErrorCode(); |
michael@0 | 5330 | } |
michael@0 | 5331 | |
michael@0 | 5332 | already_AddRefed<CDATASection> |
michael@0 | 5333 | nsIDocument::CreateCDATASection(const nsAString& aData, |
michael@0 | 5334 | ErrorResult& rv) |
michael@0 | 5335 | { |
michael@0 | 5336 | if (IsHTML()) { |
michael@0 | 5337 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 5338 | return nullptr; |
michael@0 | 5339 | } |
michael@0 | 5340 | |
michael@0 | 5341 | if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) { |
michael@0 | 5342 | rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); |
michael@0 | 5343 | return nullptr; |
michael@0 | 5344 | } |
michael@0 | 5345 | |
michael@0 | 5346 | nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager); |
michael@0 | 5347 | |
michael@0 | 5348 | // Don't notify; this node is still being created. |
michael@0 | 5349 | cdata->SetText(aData, false); |
michael@0 | 5350 | |
michael@0 | 5351 | return cdata.forget(); |
michael@0 | 5352 | } |
michael@0 | 5353 | |
michael@0 | 5354 | NS_IMETHODIMP |
michael@0 | 5355 | nsDocument::CreateProcessingInstruction(const nsAString& aTarget, |
michael@0 | 5356 | const nsAString& aData, |
michael@0 | 5357 | nsIDOMProcessingInstruction** aReturn) |
michael@0 | 5358 | { |
michael@0 | 5359 | ErrorResult rv; |
michael@0 | 5360 | *aReturn = |
michael@0 | 5361 | nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take(); |
michael@0 | 5362 | return rv.ErrorCode(); |
michael@0 | 5363 | } |
michael@0 | 5364 | |
michael@0 | 5365 | already_AddRefed<ProcessingInstruction> |
michael@0 | 5366 | nsIDocument::CreateProcessingInstruction(const nsAString& aTarget, |
michael@0 | 5367 | const nsAString& aData, |
michael@0 | 5368 | ErrorResult& rv) const |
michael@0 | 5369 | { |
michael@0 | 5370 | nsresult res = nsContentUtils::CheckQName(aTarget, false); |
michael@0 | 5371 | if (NS_FAILED(res)) { |
michael@0 | 5372 | rv.Throw(res); |
michael@0 | 5373 | return nullptr; |
michael@0 | 5374 | } |
michael@0 | 5375 | |
michael@0 | 5376 | if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) { |
michael@0 | 5377 | rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); |
michael@0 | 5378 | return nullptr; |
michael@0 | 5379 | } |
michael@0 | 5380 | |
michael@0 | 5381 | nsRefPtr<ProcessingInstruction> pi = |
michael@0 | 5382 | NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData); |
michael@0 | 5383 | |
michael@0 | 5384 | return pi.forget(); |
michael@0 | 5385 | } |
michael@0 | 5386 | |
michael@0 | 5387 | NS_IMETHODIMP |
michael@0 | 5388 | nsDocument::CreateAttribute(const nsAString& aName, |
michael@0 | 5389 | nsIDOMAttr** aReturn) |
michael@0 | 5390 | { |
michael@0 | 5391 | ErrorResult rv; |
michael@0 | 5392 | *aReturn = nsIDocument::CreateAttribute(aName, rv).take(); |
michael@0 | 5393 | return rv.ErrorCode(); |
michael@0 | 5394 | } |
michael@0 | 5395 | |
michael@0 | 5396 | already_AddRefed<Attr> |
michael@0 | 5397 | nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv) |
michael@0 | 5398 | { |
michael@0 | 5399 | WarnOnceAbout(eCreateAttribute); |
michael@0 | 5400 | |
michael@0 | 5401 | if (!mNodeInfoManager) { |
michael@0 | 5402 | rv.Throw(NS_ERROR_NOT_INITIALIZED); |
michael@0 | 5403 | return nullptr; |
michael@0 | 5404 | } |
michael@0 | 5405 | |
michael@0 | 5406 | nsresult res = nsContentUtils::CheckQName(aName, false); |
michael@0 | 5407 | if (NS_FAILED(res)) { |
michael@0 | 5408 | rv.Throw(res); |
michael@0 | 5409 | return nullptr; |
michael@0 | 5410 | } |
michael@0 | 5411 | |
michael@0 | 5412 | nsCOMPtr<nsINodeInfo> nodeInfo; |
michael@0 | 5413 | res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None, |
michael@0 | 5414 | nsIDOMNode::ATTRIBUTE_NODE, |
michael@0 | 5415 | getter_AddRefs(nodeInfo)); |
michael@0 | 5416 | if (NS_FAILED(res)) { |
michael@0 | 5417 | rv.Throw(res); |
michael@0 | 5418 | return nullptr; |
michael@0 | 5419 | } |
michael@0 | 5420 | |
michael@0 | 5421 | nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), |
michael@0 | 5422 | EmptyString(), false); |
michael@0 | 5423 | return attribute.forget(); |
michael@0 | 5424 | } |
michael@0 | 5425 | |
michael@0 | 5426 | NS_IMETHODIMP |
michael@0 | 5427 | nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI, |
michael@0 | 5428 | const nsAString & aQualifiedName, |
michael@0 | 5429 | nsIDOMAttr **aResult) |
michael@0 | 5430 | { |
michael@0 | 5431 | ErrorResult rv; |
michael@0 | 5432 | *aResult = |
michael@0 | 5433 | nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take(); |
michael@0 | 5434 | return rv.ErrorCode(); |
michael@0 | 5435 | } |
michael@0 | 5436 | |
michael@0 | 5437 | already_AddRefed<Attr> |
michael@0 | 5438 | nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI, |
michael@0 | 5439 | const nsAString& aQualifiedName, |
michael@0 | 5440 | ErrorResult& rv) |
michael@0 | 5441 | { |
michael@0 | 5442 | WarnOnceAbout(eCreateAttributeNS); |
michael@0 | 5443 | |
michael@0 | 5444 | nsCOMPtr<nsINodeInfo> nodeInfo; |
michael@0 | 5445 | rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, |
michael@0 | 5446 | aQualifiedName, |
michael@0 | 5447 | mNodeInfoManager, |
michael@0 | 5448 | nsIDOMNode::ATTRIBUTE_NODE, |
michael@0 | 5449 | getter_AddRefs(nodeInfo)); |
michael@0 | 5450 | if (rv.Failed()) { |
michael@0 | 5451 | return nullptr; |
michael@0 | 5452 | } |
michael@0 | 5453 | |
michael@0 | 5454 | nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), |
michael@0 | 5455 | EmptyString(), true); |
michael@0 | 5456 | return attribute.forget(); |
michael@0 | 5457 | } |
michael@0 | 5458 | |
michael@0 | 5459 | bool |
michael@0 | 5460 | nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp) |
michael@0 | 5461 | { |
michael@0 | 5462 | JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); |
michael@0 | 5463 | |
michael@0 | 5464 | JS::Rooted<JSObject*> global(aCx, |
michael@0 | 5465 | JS_GetGlobalForObject(aCx, &args.callee())); |
michael@0 | 5466 | nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global); |
michael@0 | 5467 | MOZ_ASSERT(window, "Should have a non-null window"); |
michael@0 | 5468 | |
michael@0 | 5469 | nsDocument* document = static_cast<nsDocument*>(window->GetDoc()); |
michael@0 | 5470 | |
michael@0 | 5471 | // Function name is the type of the custom element. |
michael@0 | 5472 | JSString* jsFunName = |
michael@0 | 5473 | JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev())); |
michael@0 | 5474 | nsDependentJSString elemName; |
michael@0 | 5475 | if (!elemName.init(aCx, jsFunName)) { |
michael@0 | 5476 | return true; |
michael@0 | 5477 | } |
michael@0 | 5478 | |
michael@0 | 5479 | nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName)); |
michael@0 | 5480 | CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom); |
michael@0 | 5481 | CustomElementDefinition* definition; |
michael@0 | 5482 | if (!document->mRegistry || |
michael@0 | 5483 | !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) { |
michael@0 | 5484 | return true; |
michael@0 | 5485 | } |
michael@0 | 5486 | |
michael@0 | 5487 | nsDependentAtomString localName(definition->mLocalName); |
michael@0 | 5488 | |
michael@0 | 5489 | nsCOMPtr<nsIContent> newElement; |
michael@0 | 5490 | nsresult rv = document->CreateElem(localName, nullptr, |
michael@0 | 5491 | definition->mNamespaceID, |
michael@0 | 5492 | getter_AddRefs(newElement)); |
michael@0 | 5493 | NS_ENSURE_SUCCESS(rv, true); |
michael@0 | 5494 | |
michael@0 | 5495 | ErrorResult errorResult; |
michael@0 | 5496 | nsCOMPtr<Element> element = do_QueryInterface(newElement); |
michael@0 | 5497 | document->SwizzleCustomElement(element, elemName, definition->mNamespaceID, |
michael@0 | 5498 | errorResult); |
michael@0 | 5499 | if (errorResult.Failed()) { |
michael@0 | 5500 | return true; |
michael@0 | 5501 | } |
michael@0 | 5502 | |
michael@0 | 5503 | rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval()); |
michael@0 | 5504 | NS_ENSURE_SUCCESS(rv, true); |
michael@0 | 5505 | |
michael@0 | 5506 | return true; |
michael@0 | 5507 | } |
michael@0 | 5508 | |
michael@0 | 5509 | bool |
michael@0 | 5510 | nsDocument::IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject) |
michael@0 | 5511 | { |
michael@0 | 5512 | JS::Rooted<JSObject*> obj(aCx, aObject); |
michael@0 | 5513 | return Preferences::GetBool("dom.webcomponents.enabled") || |
michael@0 | 5514 | IsInCertifiedApp(aCx, obj); |
michael@0 | 5515 | } |
michael@0 | 5516 | |
michael@0 | 5517 | nsresult |
michael@0 | 5518 | nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName) |
michael@0 | 5519 | { |
michael@0 | 5520 | if (!mRegistry) { |
michael@0 | 5521 | return NS_OK; |
michael@0 | 5522 | } |
michael@0 | 5523 | |
michael@0 | 5524 | nsINodeInfo* info = aElement->NodeInfo(); |
michael@0 | 5525 | |
michael@0 | 5526 | // Candidate may be a custom element through extension, |
michael@0 | 5527 | // in which case the custom element type name will not |
michael@0 | 5528 | // match the element tag name. e.g. <button is="x-button">. |
michael@0 | 5529 | nsCOMPtr<nsIAtom> typeName = aTypeName; |
michael@0 | 5530 | if (!typeName) { |
michael@0 | 5531 | typeName = info->NameAtom(); |
michael@0 | 5532 | } |
michael@0 | 5533 | |
michael@0 | 5534 | CustomElementHashKey key(info->NamespaceID(), typeName); |
michael@0 | 5535 | if (mRegistry->mCustomDefinitions.Get(&key)) { |
michael@0 | 5536 | return NS_OK; |
michael@0 | 5537 | } |
michael@0 | 5538 | |
michael@0 | 5539 | nsTArray<nsRefPtr<Element>>* unresolved; |
michael@0 | 5540 | mRegistry->mCandidatesMap.Get(&key, &unresolved); |
michael@0 | 5541 | if (!unresolved) { |
michael@0 | 5542 | unresolved = new nsTArray<nsRefPtr<Element>>(); |
michael@0 | 5543 | // Ownership of unresolved is taken by mCandidatesMap. |
michael@0 | 5544 | mRegistry->mCandidatesMap.Put(&key, unresolved); |
michael@0 | 5545 | } |
michael@0 | 5546 | |
michael@0 | 5547 | nsRefPtr<Element>* elem = unresolved->AppendElement(); |
michael@0 | 5548 | *elem = aElement; |
michael@0 | 5549 | |
michael@0 | 5550 | return NS_OK; |
michael@0 | 5551 | } |
michael@0 | 5552 | |
michael@0 | 5553 | namespace { |
michael@0 | 5554 | |
michael@0 | 5555 | class ProcessStackRunner MOZ_FINAL : public nsIRunnable |
michael@0 | 5556 | { |
michael@0 | 5557 | public: |
michael@0 | 5558 | ProcessStackRunner(bool aIsBaseQueue = false) |
michael@0 | 5559 | : mIsBaseQueue(aIsBaseQueue) |
michael@0 | 5560 | { |
michael@0 | 5561 | } |
michael@0 | 5562 | NS_DECL_ISUPPORTS |
michael@0 | 5563 | NS_IMETHOD Run() MOZ_OVERRIDE |
michael@0 | 5564 | { |
michael@0 | 5565 | nsDocument::ProcessTopElementQueue(mIsBaseQueue); |
michael@0 | 5566 | return NS_OK; |
michael@0 | 5567 | } |
michael@0 | 5568 | bool mIsBaseQueue; |
michael@0 | 5569 | }; |
michael@0 | 5570 | |
michael@0 | 5571 | NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable); |
michael@0 | 5572 | |
michael@0 | 5573 | } // anonymous namespace |
michael@0 | 5574 | |
michael@0 | 5575 | void |
michael@0 | 5576 | nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, |
michael@0 | 5577 | Element* aCustomElement, |
michael@0 | 5578 | LifecycleCallbackArgs* aArgs, |
michael@0 | 5579 | CustomElementDefinition* aDefinition) |
michael@0 | 5580 | { |
michael@0 | 5581 | if (!mRegistry) { |
michael@0 | 5582 | // The element might not belong to a document that |
michael@0 | 5583 | // has a browsing context, and thus no registry. |
michael@0 | 5584 | return; |
michael@0 | 5585 | } |
michael@0 | 5586 | |
michael@0 | 5587 | CustomElementData* elementData = aCustomElement->GetCustomElementData(); |
michael@0 | 5588 | |
michael@0 | 5589 | // Let DEFINITION be ELEMENT's definition |
michael@0 | 5590 | CustomElementDefinition* definition = aDefinition; |
michael@0 | 5591 | if (!definition) { |
michael@0 | 5592 | nsINodeInfo* info = aCustomElement->NodeInfo(); |
michael@0 | 5593 | |
michael@0 | 5594 | // Make sure we get the correct definition in case the element |
michael@0 | 5595 | // is a extended custom element e.g. <button is="x-button">. |
michael@0 | 5596 | nsCOMPtr<nsIAtom> typeAtom = elementData ? |
michael@0 | 5597 | elementData->mType.get() : info->NameAtom(); |
michael@0 | 5598 | |
michael@0 | 5599 | CustomElementHashKey key(info->NamespaceID(), typeAtom); |
michael@0 | 5600 | if (!mRegistry->mCustomDefinitions.Get(&key, &definition) || |
michael@0 | 5601 | definition->mLocalName != info->NameAtom()) { |
michael@0 | 5602 | // Trying to enqueue a callback for an element that is not |
michael@0 | 5603 | // a custom element. We are done, nothing to do. |
michael@0 | 5604 | return; |
michael@0 | 5605 | } |
michael@0 | 5606 | } |
michael@0 | 5607 | |
michael@0 | 5608 | if (!elementData) { |
michael@0 | 5609 | // Create the custom element data the first time |
michael@0 | 5610 | // that we try to enqueue a callback. |
michael@0 | 5611 | elementData = new CustomElementData(definition->mType); |
michael@0 | 5612 | // aCustomElement takes ownership of elementData |
michael@0 | 5613 | aCustomElement->SetCustomElementData(elementData); |
michael@0 | 5614 | MOZ_ASSERT(aType == nsIDocument::eCreated, |
michael@0 | 5615 | "First callback should be the created callback"); |
michael@0 | 5616 | } |
michael@0 | 5617 | |
michael@0 | 5618 | // Let CALLBACK be the callback associated with the key NAME in CALLBACKS. |
michael@0 | 5619 | CallbackFunction* func = nullptr; |
michael@0 | 5620 | switch (aType) { |
michael@0 | 5621 | case nsIDocument::eCreated: |
michael@0 | 5622 | if (definition->mCallbacks->mCreatedCallback.WasPassed()) { |
michael@0 | 5623 | func = definition->mCallbacks->mCreatedCallback.Value(); |
michael@0 | 5624 | } |
michael@0 | 5625 | break; |
michael@0 | 5626 | |
michael@0 | 5627 | case nsIDocument::eAttached: |
michael@0 | 5628 | if (definition->mCallbacks->mAttachedCallback.WasPassed()) { |
michael@0 | 5629 | func = definition->mCallbacks->mAttachedCallback.Value(); |
michael@0 | 5630 | } |
michael@0 | 5631 | break; |
michael@0 | 5632 | |
michael@0 | 5633 | case nsIDocument::eDetached: |
michael@0 | 5634 | if (definition->mCallbacks->mDetachedCallback.WasPassed()) { |
michael@0 | 5635 | func = definition->mCallbacks->mDetachedCallback.Value(); |
michael@0 | 5636 | } |
michael@0 | 5637 | break; |
michael@0 | 5638 | |
michael@0 | 5639 | case nsIDocument::eAttributeChanged: |
michael@0 | 5640 | if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) { |
michael@0 | 5641 | func = definition->mCallbacks->mAttributeChangedCallback.Value(); |
michael@0 | 5642 | } |
michael@0 | 5643 | break; |
michael@0 | 5644 | } |
michael@0 | 5645 | |
michael@0 | 5646 | // If there is no such callback, stop. |
michael@0 | 5647 | if (!func) { |
michael@0 | 5648 | return; |
michael@0 | 5649 | } |
michael@0 | 5650 | |
michael@0 | 5651 | if (aType == nsIDocument::eCreated) { |
michael@0 | 5652 | elementData->mCreatedCallbackInvoked = false; |
michael@0 | 5653 | } else if (!elementData->mCreatedCallbackInvoked) { |
michael@0 | 5654 | // Callbacks other than created callback must not be enqueued |
michael@0 | 5655 | // until after the created callback has been invoked. |
michael@0 | 5656 | return; |
michael@0 | 5657 | } |
michael@0 | 5658 | |
michael@0 | 5659 | // Add CALLBACK to ELEMENT's callback queue. |
michael@0 | 5660 | CustomElementCallback* callback = new CustomElementCallback(aCustomElement, |
michael@0 | 5661 | aType, |
michael@0 | 5662 | func, |
michael@0 | 5663 | elementData); |
michael@0 | 5664 | // Ownership of callback is taken by mCallbackQueue. |
michael@0 | 5665 | elementData->mCallbackQueue.AppendElement(callback); |
michael@0 | 5666 | if (aArgs) { |
michael@0 | 5667 | callback->SetArgs(*aArgs); |
michael@0 | 5668 | } |
michael@0 | 5669 | |
michael@0 | 5670 | if (!elementData->mElementIsBeingCreated) { |
michael@0 | 5671 | CustomElementData* lastData = |
michael@0 | 5672 | sProcessingStack.ref().SafeLastElement(nullptr); |
michael@0 | 5673 | |
michael@0 | 5674 | // A new element queue needs to be pushed if the queue at the |
michael@0 | 5675 | // top of the stack is associated with another microtask level. |
michael@0 | 5676 | // Don't push a queue for the level 0 microtask (base element queue) |
michael@0 | 5677 | // because we don't want to process the queue until the |
michael@0 | 5678 | // microtask checkpoint. |
michael@0 | 5679 | bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 && |
michael@0 | 5680 | (!lastData || lastData->mAssociatedMicroTask < |
michael@0 | 5681 | static_cast<int32_t>(nsContentUtils::MicroTaskLevel())); |
michael@0 | 5682 | |
michael@0 | 5683 | // Push a new element queue onto the processing stack when appropriate |
michael@0 | 5684 | // (when we enter a new microtask). |
michael@0 | 5685 | if (shouldPushElementQueue) { |
michael@0 | 5686 | // Push a sentinel value on the processing stack to mark the |
michael@0 | 5687 | // boundary between the element queues. |
michael@0 | 5688 | sProcessingStack.ref().AppendElement((CustomElementData*) nullptr); |
michael@0 | 5689 | } |
michael@0 | 5690 | |
michael@0 | 5691 | sProcessingStack.ref().AppendElement(elementData); |
michael@0 | 5692 | elementData->mAssociatedMicroTask = |
michael@0 | 5693 | static_cast<int32_t>(nsContentUtils::MicroTaskLevel()); |
michael@0 | 5694 | |
michael@0 | 5695 | // Add a script runner to pop and process the element queue at |
michael@0 | 5696 | // the top of the processing stack. |
michael@0 | 5697 | if (shouldPushElementQueue) { |
michael@0 | 5698 | // Lifecycle callbacks enqueued by user agent implementation |
michael@0 | 5699 | // should be invoked prior to returning control back to script. |
michael@0 | 5700 | // Create a script runner to process the top of the processing |
michael@0 | 5701 | // stack as soon as it is safe to run script. |
michael@0 | 5702 | nsContentUtils::AddScriptRunner(new ProcessStackRunner()); |
michael@0 | 5703 | } |
michael@0 | 5704 | } |
michael@0 | 5705 | } |
michael@0 | 5706 | |
michael@0 | 5707 | // static |
michael@0 | 5708 | void |
michael@0 | 5709 | nsDocument::ProcessBaseElementQueue() |
michael@0 | 5710 | { |
michael@0 | 5711 | // Prevent re-entrance. Also, if a microtask checkpoint is reached |
michael@0 | 5712 | // and there is no processing stack to process, then we are done. |
michael@0 | 5713 | if (sProcessingBaseElementQueue || sProcessingStack.empty()) { |
michael@0 | 5714 | return; |
michael@0 | 5715 | } |
michael@0 | 5716 | |
michael@0 | 5717 | MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0); |
michael@0 | 5718 | sProcessingBaseElementQueue = true; |
michael@0 | 5719 | nsContentUtils::AddScriptRunner(new ProcessStackRunner(true)); |
michael@0 | 5720 | } |
michael@0 | 5721 | |
michael@0 | 5722 | // static |
michael@0 | 5723 | void |
michael@0 | 5724 | nsDocument::ProcessTopElementQueue(bool aIsBaseQueue) |
michael@0 | 5725 | { |
michael@0 | 5726 | MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); |
michael@0 | 5727 | |
michael@0 | 5728 | nsTArray<CustomElementData*>& stack = sProcessingStack.ref(); |
michael@0 | 5729 | uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr); |
michael@0 | 5730 | |
michael@0 | 5731 | if (aIsBaseQueue && firstQueue != 0) { |
michael@0 | 5732 | return; |
michael@0 | 5733 | } |
michael@0 | 5734 | |
michael@0 | 5735 | for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) { |
michael@0 | 5736 | // Callback queue may have already been processed in an earlier |
michael@0 | 5737 | // element queue or in an element queue that was popped |
michael@0 | 5738 | // off more recently. |
michael@0 | 5739 | if (stack[i]->mAssociatedMicroTask != -1) { |
michael@0 | 5740 | stack[i]->RunCallbackQueue(); |
michael@0 | 5741 | stack[i]->mAssociatedMicroTask = -1; |
michael@0 | 5742 | } |
michael@0 | 5743 | } |
michael@0 | 5744 | |
michael@0 | 5745 | // If this was actually the base element queue, don't bother trying to pop |
michael@0 | 5746 | // the first "queue" marker (sentinel). |
michael@0 | 5747 | if (firstQueue != 0) { |
michael@0 | 5748 | stack.SetLength(firstQueue); |
michael@0 | 5749 | } else { |
michael@0 | 5750 | // Don't pop sentinel for base element queue. |
michael@0 | 5751 | stack.SetLength(1); |
michael@0 | 5752 | sProcessingBaseElementQueue = false; |
michael@0 | 5753 | } |
michael@0 | 5754 | } |
michael@0 | 5755 | |
michael@0 | 5756 | bool |
michael@0 | 5757 | nsDocument::RegisterEnabled() |
michael@0 | 5758 | { |
michael@0 | 5759 | static bool sPrefValue = |
michael@0 | 5760 | Preferences::GetBool("dom.webcomponents.enabled", false); |
michael@0 | 5761 | return sPrefValue; |
michael@0 | 5762 | } |
michael@0 | 5763 | |
michael@0 | 5764 | // static |
michael@0 | 5765 | Maybe<nsTArray<mozilla::dom::CustomElementData*>> |
michael@0 | 5766 | nsDocument::sProcessingStack; |
michael@0 | 5767 | |
michael@0 | 5768 | // static |
michael@0 | 5769 | bool |
michael@0 | 5770 | nsDocument::sProcessingBaseElementQueue; |
michael@0 | 5771 | |
michael@0 | 5772 | void |
michael@0 | 5773 | nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType, |
michael@0 | 5774 | const ElementRegistrationOptions& aOptions, |
michael@0 | 5775 | JS::MutableHandle<JSObject*> aRetval, |
michael@0 | 5776 | ErrorResult& rv) |
michael@0 | 5777 | { |
michael@0 | 5778 | if (!mRegistry) { |
michael@0 | 5779 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 5780 | return; |
michael@0 | 5781 | } |
michael@0 | 5782 | |
michael@0 | 5783 | Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions; |
michael@0 | 5784 | |
michael@0 | 5785 | // Unconditionally convert TYPE to lowercase. |
michael@0 | 5786 | nsAutoString lcType; |
michael@0 | 5787 | nsContentUtils::ASCIIToLower(aType, lcType); |
michael@0 | 5788 | |
michael@0 | 5789 | // Only convert NAME to lowercase in HTML documents. Note that NAME is |
michael@0 | 5790 | // options.extends. |
michael@0 | 5791 | nsAutoString lcName; |
michael@0 | 5792 | if (IsHTML()) { |
michael@0 | 5793 | nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName); |
michael@0 | 5794 | } else { |
michael@0 | 5795 | lcName.Assign(aOptions.mExtends); |
michael@0 | 5796 | } |
michael@0 | 5797 | |
michael@0 | 5798 | nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType)); |
michael@0 | 5799 | if (!nsContentUtils::IsCustomElementName(typeAtom)) { |
michael@0 | 5800 | rv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
michael@0 | 5801 | return; |
michael@0 | 5802 | } |
michael@0 | 5803 | |
michael@0 | 5804 | // If there already exists a definition with the same TYPE, set ERROR to |
michael@0 | 5805 | // DuplicateDefinition and stop. |
michael@0 | 5806 | // Note that we need to find existing custom elements from either namespace. |
michael@0 | 5807 | CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom); |
michael@0 | 5808 | if (definitions.Get(&duplicateFinder)) { |
michael@0 | 5809 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 5810 | return; |
michael@0 | 5811 | } |
michael@0 | 5812 | |
michael@0 | 5813 | nsIGlobalObject* sgo = GetScopeObject(); |
michael@0 | 5814 | if (!sgo) { |
michael@0 | 5815 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5816 | return; |
michael@0 | 5817 | } |
michael@0 | 5818 | JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject()); |
michael@0 | 5819 | |
michael@0 | 5820 | JSAutoCompartment ac(aCx, global); |
michael@0 | 5821 | |
michael@0 | 5822 | JS::Handle<JSObject*> htmlProto( |
michael@0 | 5823 | HTMLElementBinding::GetProtoObject(aCx, global)); |
michael@0 | 5824 | if (!htmlProto) { |
michael@0 | 5825 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 5826 | return; |
michael@0 | 5827 | } |
michael@0 | 5828 | |
michael@0 | 5829 | int32_t namespaceID = kNameSpaceID_XHTML; |
michael@0 | 5830 | JS::Rooted<JSObject*> protoObject(aCx); |
michael@0 | 5831 | if (!aOptions.mPrototype) { |
michael@0 | 5832 | protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr()); |
michael@0 | 5833 | if (!protoObject) { |
michael@0 | 5834 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5835 | return; |
michael@0 | 5836 | } |
michael@0 | 5837 | } else { |
michael@0 | 5838 | // If a prototype is provided, we must check to ensure that it is from the |
michael@0 | 5839 | // same browsing context as us. |
michael@0 | 5840 | protoObject = aOptions.mPrototype; |
michael@0 | 5841 | if (JS_GetGlobalForObject(aCx, protoObject) != global) { |
michael@0 | 5842 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 5843 | return; |
michael@0 | 5844 | } |
michael@0 | 5845 | |
michael@0 | 5846 | // If PROTOTYPE is already an interface prototype object for any interface |
michael@0 | 5847 | // object or PROTOTYPE has a non-configurable property named constructor, |
michael@0 | 5848 | // throw a NotSupportedError and stop. |
michael@0 | 5849 | const js::Class* clasp = js::GetObjectClass(protoObject); |
michael@0 | 5850 | if (IsDOMIfaceAndProtoClass(clasp)) { |
michael@0 | 5851 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 5852 | return; |
michael@0 | 5853 | } |
michael@0 | 5854 | |
michael@0 | 5855 | JS::Rooted<JSPropertyDescriptor> descRoot(aCx); |
michael@0 | 5856 | JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot); |
michael@0 | 5857 | if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) { |
michael@0 | 5858 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5859 | return; |
michael@0 | 5860 | } |
michael@0 | 5861 | |
michael@0 | 5862 | // Check if non-configurable |
michael@0 | 5863 | if (desc.isPermanent()) { |
michael@0 | 5864 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 5865 | return; |
michael@0 | 5866 | } |
michael@0 | 5867 | |
michael@0 | 5868 | JS::Handle<JSObject*> svgProto( |
michael@0 | 5869 | SVGElementBinding::GetProtoObject(aCx, global)); |
michael@0 | 5870 | if (!svgProto) { |
michael@0 | 5871 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 5872 | return; |
michael@0 | 5873 | } |
michael@0 | 5874 | |
michael@0 | 5875 | JS::Rooted<JSObject*> protoProto(aCx, protoObject); |
michael@0 | 5876 | |
michael@0 | 5877 | // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG |
michael@0 | 5878 | // Namespace. |
michael@0 | 5879 | while (protoProto) { |
michael@0 | 5880 | if (protoProto == htmlProto) { |
michael@0 | 5881 | break; |
michael@0 | 5882 | } |
michael@0 | 5883 | |
michael@0 | 5884 | if (protoProto == svgProto) { |
michael@0 | 5885 | namespaceID = kNameSpaceID_SVG; |
michael@0 | 5886 | break; |
michael@0 | 5887 | } |
michael@0 | 5888 | |
michael@0 | 5889 | if (!JS_GetPrototype(aCx, protoProto, &protoProto)) { |
michael@0 | 5890 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5891 | return; |
michael@0 | 5892 | } |
michael@0 | 5893 | } |
michael@0 | 5894 | } |
michael@0 | 5895 | |
michael@0 | 5896 | // If name was provided and not null... |
michael@0 | 5897 | nsCOMPtr<nsIAtom> nameAtom; |
michael@0 | 5898 | if (!lcName.IsEmpty()) { |
michael@0 | 5899 | // Let BASE be the element interface for NAME and NAMESPACE. |
michael@0 | 5900 | bool known = false; |
michael@0 | 5901 | nameAtom = do_GetAtom(lcName); |
michael@0 | 5902 | if (namespaceID == kNameSpaceID_XHTML) { |
michael@0 | 5903 | nsIParserService* ps = nsContentUtils::GetParserService(); |
michael@0 | 5904 | if (!ps) { |
michael@0 | 5905 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5906 | return; |
michael@0 | 5907 | } |
michael@0 | 5908 | |
michael@0 | 5909 | known = |
michael@0 | 5910 | ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined; |
michael@0 | 5911 | } else { |
michael@0 | 5912 | known = SVGElementFactory::Exists(nameAtom); |
michael@0 | 5913 | } |
michael@0 | 5914 | |
michael@0 | 5915 | // If BASE does not exist or is an interface for a custom element, set ERROR |
michael@0 | 5916 | // to InvalidName and stop. |
michael@0 | 5917 | // If BASE exists, then it cannot be an interface for a custom element. |
michael@0 | 5918 | if (!known) { |
michael@0 | 5919 | rv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
michael@0 | 5920 | return; |
michael@0 | 5921 | } |
michael@0 | 5922 | } else { |
michael@0 | 5923 | // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop. |
michael@0 | 5924 | if (namespaceID == kNameSpaceID_SVG) { |
michael@0 | 5925 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 5926 | return; |
michael@0 | 5927 | } |
michael@0 | 5928 | |
michael@0 | 5929 | nameAtom = typeAtom; |
michael@0 | 5930 | } |
michael@0 | 5931 | |
michael@0 | 5932 | nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks()); |
michael@0 | 5933 | JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject)); |
michael@0 | 5934 | if (!callbacksHolder->Init(aCx, rootedv)) { |
michael@0 | 5935 | rv.Throw(NS_ERROR_FAILURE); |
michael@0 | 5936 | return; |
michael@0 | 5937 | } |
michael@0 | 5938 | |
michael@0 | 5939 | // Associate the definition with the custom element. |
michael@0 | 5940 | CustomElementHashKey key(namespaceID, typeAtom); |
michael@0 | 5941 | LifecycleCallbacks* callbacks = callbacksHolder.forget(); |
michael@0 | 5942 | CustomElementDefinition* definition = |
michael@0 | 5943 | new CustomElementDefinition(protoObject, |
michael@0 | 5944 | typeAtom, |
michael@0 | 5945 | nameAtom, |
michael@0 | 5946 | callbacks, |
michael@0 | 5947 | namespaceID, |
michael@0 | 5948 | 0 /* TODO dependent on HTML imports. Bug 877072 */); |
michael@0 | 5949 | definitions.Put(&key, definition); |
michael@0 | 5950 | |
michael@0 | 5951 | // Do element upgrade. |
michael@0 | 5952 | nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates; |
michael@0 | 5953 | mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates); |
michael@0 | 5954 | if (candidates) { |
michael@0 | 5955 | for (size_t i = 0; i < candidates->Length(); ++i) { |
michael@0 | 5956 | Element *elem = candidates->ElementAt(i); |
michael@0 | 5957 | |
michael@0 | 5958 | // Make sure that the element name matches the name in the definition. |
michael@0 | 5959 | // (e.g. a definition for x-button extending button should match |
michael@0 | 5960 | // <button is="x-button"> but not <x-button>. |
michael@0 | 5961 | if (elem->NodeInfo()->NameAtom() != nameAtom) { |
michael@0 | 5962 | // Skip over this element because definition does not apply. |
michael@0 | 5963 | continue; |
michael@0 | 5964 | } |
michael@0 | 5965 | |
michael@0 | 5966 | nsWrapperCache* cache; |
michael@0 | 5967 | CallQueryInterface(elem, &cache); |
michael@0 | 5968 | MOZ_ASSERT(cache, "Element doesn't support wrapper cache?"); |
michael@0 | 5969 | |
michael@0 | 5970 | JS::RootedObject wrapper(aCx); |
michael@0 | 5971 | if ((wrapper = cache->GetWrapper())) { |
michael@0 | 5972 | if (!JS_SetPrototype(aCx, wrapper, protoObject)) { |
michael@0 | 5973 | continue; |
michael@0 | 5974 | } |
michael@0 | 5975 | } |
michael@0 | 5976 | |
michael@0 | 5977 | EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition); |
michael@0 | 5978 | if (elem->GetCurrentDoc()) { |
michael@0 | 5979 | // Normally callbacks can not be enqueued until the created |
michael@0 | 5980 | // callback has been invoked, however, the attached callback |
michael@0 | 5981 | // in element upgrade is an exception so pretend the created |
michael@0 | 5982 | // callback has been invoked. |
michael@0 | 5983 | elem->GetCustomElementData()->mCreatedCallbackInvoked = true; |
michael@0 | 5984 | |
michael@0 | 5985 | EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition); |
michael@0 | 5986 | } |
michael@0 | 5987 | } |
michael@0 | 5988 | } |
michael@0 | 5989 | |
michael@0 | 5990 | // Create constructor to return. Store the name of the custom element as the |
michael@0 | 5991 | // name of the function. |
michael@0 | 5992 | JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0, |
michael@0 | 5993 | JSFUN_CONSTRUCTOR, JS::NullPtr(), |
michael@0 | 5994 | NS_ConvertUTF16toUTF8(lcType).get()); |
michael@0 | 5995 | if (!constructor) { |
michael@0 | 5996 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 5997 | return; |
michael@0 | 5998 | } |
michael@0 | 5999 | |
michael@0 | 6000 | aRetval.set(JS_GetFunctionObject(constructor)); |
michael@0 | 6001 | } |
michael@0 | 6002 | |
michael@0 | 6003 | void |
michael@0 | 6004 | nsDocument::UseRegistryFromDocument(nsIDocument* aDocument) |
michael@0 | 6005 | { |
michael@0 | 6006 | nsDocument* doc = static_cast<nsDocument*>(aDocument); |
michael@0 | 6007 | MOZ_ASSERT(!mRegistry, "There should be no existing registry."); |
michael@0 | 6008 | mRegistry = doc->mRegistry; |
michael@0 | 6009 | } |
michael@0 | 6010 | |
michael@0 | 6011 | NS_IMETHODIMP |
michael@0 | 6012 | nsDocument::GetElementsByTagName(const nsAString& aTagname, |
michael@0 | 6013 | nsIDOMNodeList** aReturn) |
michael@0 | 6014 | { |
michael@0 | 6015 | nsRefPtr<nsContentList> list = GetElementsByTagName(aTagname); |
michael@0 | 6016 | NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 6017 | |
michael@0 | 6018 | // transfer ref to aReturn |
michael@0 | 6019 | list.forget(aReturn); |
michael@0 | 6020 | return NS_OK; |
michael@0 | 6021 | } |
michael@0 | 6022 | |
michael@0 | 6023 | already_AddRefed<nsContentList> |
michael@0 | 6024 | nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, |
michael@0 | 6025 | const nsAString& aLocalName, |
michael@0 | 6026 | ErrorResult& aResult) |
michael@0 | 6027 | { |
michael@0 | 6028 | int32_t nameSpaceId = kNameSpaceID_Wildcard; |
michael@0 | 6029 | |
michael@0 | 6030 | if (!aNamespaceURI.EqualsLiteral("*")) { |
michael@0 | 6031 | aResult = |
michael@0 | 6032 | nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
michael@0 | 6033 | nameSpaceId); |
michael@0 | 6034 | if (aResult.Failed()) { |
michael@0 | 6035 | return nullptr; |
michael@0 | 6036 | } |
michael@0 | 6037 | } |
michael@0 | 6038 | |
michael@0 | 6039 | NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!"); |
michael@0 | 6040 | |
michael@0 | 6041 | return NS_GetContentList(this, nameSpaceId, aLocalName); |
michael@0 | 6042 | } |
michael@0 | 6043 | |
michael@0 | 6044 | NS_IMETHODIMP |
michael@0 | 6045 | nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, |
michael@0 | 6046 | const nsAString& aLocalName, |
michael@0 | 6047 | nsIDOMNodeList** aReturn) |
michael@0 | 6048 | { |
michael@0 | 6049 | ErrorResult rv; |
michael@0 | 6050 | nsRefPtr<nsContentList> list = |
michael@0 | 6051 | nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv); |
michael@0 | 6052 | if (rv.Failed()) { |
michael@0 | 6053 | return rv.ErrorCode(); |
michael@0 | 6054 | } |
michael@0 | 6055 | |
michael@0 | 6056 | // transfer ref to aReturn |
michael@0 | 6057 | list.forget(aReturn); |
michael@0 | 6058 | return NS_OK; |
michael@0 | 6059 | } |
michael@0 | 6060 | |
michael@0 | 6061 | NS_IMETHODIMP |
michael@0 | 6062 | nsDocument::GetAsync(bool *aAsync) |
michael@0 | 6063 | { |
michael@0 | 6064 | NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!"); |
michael@0 | 6065 | |
michael@0 | 6066 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 6067 | } |
michael@0 | 6068 | |
michael@0 | 6069 | NS_IMETHODIMP |
michael@0 | 6070 | nsDocument::SetAsync(bool aAsync) |
michael@0 | 6071 | { |
michael@0 | 6072 | NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!"); |
michael@0 | 6073 | |
michael@0 | 6074 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 6075 | } |
michael@0 | 6076 | |
michael@0 | 6077 | NS_IMETHODIMP |
michael@0 | 6078 | nsDocument::Load(const nsAString& aUrl, bool *aReturn) |
michael@0 | 6079 | { |
michael@0 | 6080 | NS_ERROR("nsDocument::Load() should be overriden by subclass!"); |
michael@0 | 6081 | |
michael@0 | 6082 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 6083 | } |
michael@0 | 6084 | |
michael@0 | 6085 | NS_IMETHODIMP |
michael@0 | 6086 | nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets) |
michael@0 | 6087 | { |
michael@0 | 6088 | NS_ADDREF(*aStyleSheets = StyleSheets()); |
michael@0 | 6089 | return NS_OK; |
michael@0 | 6090 | } |
michael@0 | 6091 | |
michael@0 | 6092 | StyleSheetList* |
michael@0 | 6093 | nsDocument::StyleSheets() |
michael@0 | 6094 | { |
michael@0 | 6095 | if (!mDOMStyleSheets) { |
michael@0 | 6096 | mDOMStyleSheets = new nsDOMStyleSheetList(this); |
michael@0 | 6097 | } |
michael@0 | 6098 | return mDOMStyleSheets; |
michael@0 | 6099 | } |
michael@0 | 6100 | |
michael@0 | 6101 | NS_IMETHODIMP |
michael@0 | 6102 | nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet) |
michael@0 | 6103 | { |
michael@0 | 6104 | nsIDocument::GetSelectedStyleSheetSet(aSheetSet); |
michael@0 | 6105 | return NS_OK; |
michael@0 | 6106 | } |
michael@0 | 6107 | |
michael@0 | 6108 | void |
michael@0 | 6109 | nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet) |
michael@0 | 6110 | { |
michael@0 | 6111 | aSheetSet.Truncate(); |
michael@0 | 6112 | |
michael@0 | 6113 | // Look through our sheets, find the selected set title |
michael@0 | 6114 | int32_t count = GetNumberOfStyleSheets(); |
michael@0 | 6115 | nsAutoString title; |
michael@0 | 6116 | for (int32_t index = 0; index < count; index++) { |
michael@0 | 6117 | nsIStyleSheet* sheet = GetStyleSheetAt(index); |
michael@0 | 6118 | NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
michael@0 | 6119 | |
michael@0 | 6120 | nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet); |
michael@0 | 6121 | NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet"); |
michael@0 | 6122 | bool disabled; |
michael@0 | 6123 | domSheet->GetDisabled(&disabled); |
michael@0 | 6124 | if (disabled) { |
michael@0 | 6125 | // Disabled sheets don't affect the currently selected set |
michael@0 | 6126 | continue; |
michael@0 | 6127 | } |
michael@0 | 6128 | |
michael@0 | 6129 | sheet->GetTitle(title); |
michael@0 | 6130 | |
michael@0 | 6131 | if (aSheetSet.IsEmpty()) { |
michael@0 | 6132 | aSheetSet = title; |
michael@0 | 6133 | } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) { |
michael@0 | 6134 | // Sheets from multiple sets enabled; return null string, per spec. |
michael@0 | 6135 | SetDOMStringToNull(aSheetSet); |
michael@0 | 6136 | return; |
michael@0 | 6137 | } |
michael@0 | 6138 | } |
michael@0 | 6139 | } |
michael@0 | 6140 | |
michael@0 | 6141 | NS_IMETHODIMP |
michael@0 | 6142 | nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet) |
michael@0 | 6143 | { |
michael@0 | 6144 | SetSelectedStyleSheetSet(aSheetSet); |
michael@0 | 6145 | return NS_OK; |
michael@0 | 6146 | } |
michael@0 | 6147 | |
michael@0 | 6148 | void |
michael@0 | 6149 | nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet) |
michael@0 | 6150 | { |
michael@0 | 6151 | if (DOMStringIsNull(aSheetSet)) { |
michael@0 | 6152 | return; |
michael@0 | 6153 | } |
michael@0 | 6154 | |
michael@0 | 6155 | // Must update mLastStyleSheetSet before doing anything else with stylesheets |
michael@0 | 6156 | // or CSSLoaders. |
michael@0 | 6157 | mLastStyleSheetSet = aSheetSet; |
michael@0 | 6158 | EnableStyleSheetsForSetInternal(aSheetSet, true); |
michael@0 | 6159 | } |
michael@0 | 6160 | |
michael@0 | 6161 | NS_IMETHODIMP |
michael@0 | 6162 | nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet) |
michael@0 | 6163 | { |
michael@0 | 6164 | nsString sheetSet; |
michael@0 | 6165 | GetLastStyleSheetSet(sheetSet); |
michael@0 | 6166 | aSheetSet = sheetSet; |
michael@0 | 6167 | return NS_OK; |
michael@0 | 6168 | } |
michael@0 | 6169 | |
michael@0 | 6170 | void |
michael@0 | 6171 | nsDocument::GetLastStyleSheetSet(nsString& aSheetSet) |
michael@0 | 6172 | { |
michael@0 | 6173 | aSheetSet = mLastStyleSheetSet; |
michael@0 | 6174 | } |
michael@0 | 6175 | |
michael@0 | 6176 | NS_IMETHODIMP |
michael@0 | 6177 | nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet) |
michael@0 | 6178 | { |
michael@0 | 6179 | nsIDocument::GetPreferredStyleSheetSet(aSheetSet); |
michael@0 | 6180 | return NS_OK; |
michael@0 | 6181 | } |
michael@0 | 6182 | |
michael@0 | 6183 | void |
michael@0 | 6184 | nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet) |
michael@0 | 6185 | { |
michael@0 | 6186 | GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet); |
michael@0 | 6187 | } |
michael@0 | 6188 | |
michael@0 | 6189 | NS_IMETHODIMP |
michael@0 | 6190 | nsDocument::GetStyleSheetSets(nsISupports** aList) |
michael@0 | 6191 | { |
michael@0 | 6192 | NS_ADDREF(*aList = StyleSheetSets()); |
michael@0 | 6193 | return NS_OK; |
michael@0 | 6194 | } |
michael@0 | 6195 | |
michael@0 | 6196 | DOMStringList* |
michael@0 | 6197 | nsDocument::StyleSheetSets() |
michael@0 | 6198 | { |
michael@0 | 6199 | if (!mStyleSheetSetList) { |
michael@0 | 6200 | mStyleSheetSetList = new nsDOMStyleSheetSetList(this); |
michael@0 | 6201 | } |
michael@0 | 6202 | return mStyleSheetSetList; |
michael@0 | 6203 | } |
michael@0 | 6204 | |
michael@0 | 6205 | NS_IMETHODIMP |
michael@0 | 6206 | nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet) |
michael@0 | 6207 | { |
michael@0 | 6208 | EnableStyleSheetsForSet(aSheetSet); |
michael@0 | 6209 | return NS_OK; |
michael@0 | 6210 | } |
michael@0 | 6211 | |
michael@0 | 6212 | void |
michael@0 | 6213 | nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet) |
michael@0 | 6214 | { |
michael@0 | 6215 | // Per spec, passing in null is a no-op. |
michael@0 | 6216 | if (!DOMStringIsNull(aSheetSet)) { |
michael@0 | 6217 | // Note: must make sure to not change the CSSLoader's preferred sheet -- |
michael@0 | 6218 | // that value should be equal to either our lastStyleSheetSet (if that's |
michael@0 | 6219 | // non-null) or to our preferredStyleSheetSet. And this method doesn't |
michael@0 | 6220 | // change either of those. |
michael@0 | 6221 | EnableStyleSheetsForSetInternal(aSheetSet, false); |
michael@0 | 6222 | } |
michael@0 | 6223 | } |
michael@0 | 6224 | |
michael@0 | 6225 | void |
michael@0 | 6226 | nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet, |
michael@0 | 6227 | bool aUpdateCSSLoader) |
michael@0 | 6228 | { |
michael@0 | 6229 | BeginUpdate(UPDATE_STYLE); |
michael@0 | 6230 | int32_t count = GetNumberOfStyleSheets(); |
michael@0 | 6231 | nsAutoString title; |
michael@0 | 6232 | for (int32_t index = 0; index < count; index++) { |
michael@0 | 6233 | nsIStyleSheet* sheet = GetStyleSheetAt(index); |
michael@0 | 6234 | NS_ASSERTION(sheet, "Null sheet in sheet list!"); |
michael@0 | 6235 | sheet->GetTitle(title); |
michael@0 | 6236 | if (!title.IsEmpty()) { |
michael@0 | 6237 | sheet->SetEnabled(title.Equals(aSheetSet)); |
michael@0 | 6238 | } |
michael@0 | 6239 | } |
michael@0 | 6240 | if (aUpdateCSSLoader) { |
michael@0 | 6241 | CSSLoader()->SetPreferredSheet(aSheetSet); |
michael@0 | 6242 | } |
michael@0 | 6243 | EndUpdate(UPDATE_STYLE); |
michael@0 | 6244 | } |
michael@0 | 6245 | |
michael@0 | 6246 | NS_IMETHODIMP |
michael@0 | 6247 | nsDocument::GetCharacterSet(nsAString& aCharacterSet) |
michael@0 | 6248 | { |
michael@0 | 6249 | nsIDocument::GetCharacterSet(aCharacterSet); |
michael@0 | 6250 | return NS_OK; |
michael@0 | 6251 | } |
michael@0 | 6252 | |
michael@0 | 6253 | void |
michael@0 | 6254 | nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const |
michael@0 | 6255 | { |
michael@0 | 6256 | CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet); |
michael@0 | 6257 | } |
michael@0 | 6258 | |
michael@0 | 6259 | NS_IMETHODIMP |
michael@0 | 6260 | nsDocument::ImportNode(nsIDOMNode* aImportedNode, |
michael@0 | 6261 | bool aDeep, |
michael@0 | 6262 | uint8_t aArgc, |
michael@0 | 6263 | nsIDOMNode** aResult) |
michael@0 | 6264 | { |
michael@0 | 6265 | if (aArgc == 0) { |
michael@0 | 6266 | aDeep = true; |
michael@0 | 6267 | } |
michael@0 | 6268 | |
michael@0 | 6269 | *aResult = nullptr; |
michael@0 | 6270 | |
michael@0 | 6271 | nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode); |
michael@0 | 6272 | NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED); |
michael@0 | 6273 | |
michael@0 | 6274 | ErrorResult rv; |
michael@0 | 6275 | nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv); |
michael@0 | 6276 | if (rv.Failed()) { |
michael@0 | 6277 | return rv.ErrorCode(); |
michael@0 | 6278 | } |
michael@0 | 6279 | |
michael@0 | 6280 | NS_ADDREF(*aResult = result->AsDOMNode()); |
michael@0 | 6281 | return NS_OK; |
michael@0 | 6282 | } |
michael@0 | 6283 | |
michael@0 | 6284 | already_AddRefed<nsINode> |
michael@0 | 6285 | nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const |
michael@0 | 6286 | { |
michael@0 | 6287 | nsINode* imported = &aNode; |
michael@0 | 6288 | |
michael@0 | 6289 | switch (imported->NodeType()) { |
michael@0 | 6290 | case nsIDOMNode::ATTRIBUTE_NODE: |
michael@0 | 6291 | case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: |
michael@0 | 6292 | case nsIDOMNode::ELEMENT_NODE: |
michael@0 | 6293 | case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: |
michael@0 | 6294 | case nsIDOMNode::TEXT_NODE: |
michael@0 | 6295 | case nsIDOMNode::CDATA_SECTION_NODE: |
michael@0 | 6296 | case nsIDOMNode::COMMENT_NODE: |
michael@0 | 6297 | case nsIDOMNode::DOCUMENT_TYPE_NODE: |
michael@0 | 6298 | { |
michael@0 | 6299 | nsCOMPtr<nsINode> newNode; |
michael@0 | 6300 | nsCOMArray<nsINode> nodesWithProperties; |
michael@0 | 6301 | rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, |
michael@0 | 6302 | nodesWithProperties, getter_AddRefs(newNode)); |
michael@0 | 6303 | if (rv.Failed()) { |
michael@0 | 6304 | return nullptr; |
michael@0 | 6305 | } |
michael@0 | 6306 | |
michael@0 | 6307 | nsIDocument *ownerDoc = imported->OwnerDoc(); |
michael@0 | 6308 | rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, ownerDoc, |
michael@0 | 6309 | nsIDOMUserDataHandler::NODE_IMPORTED, |
michael@0 | 6310 | true); |
michael@0 | 6311 | if (rv.Failed()) { |
michael@0 | 6312 | return nullptr; |
michael@0 | 6313 | } |
michael@0 | 6314 | |
michael@0 | 6315 | return newNode.forget(); |
michael@0 | 6316 | } |
michael@0 | 6317 | default: |
michael@0 | 6318 | { |
michael@0 | 6319 | NS_WARNING("Don't know how to clone this nodetype for importNode."); |
michael@0 | 6320 | |
michael@0 | 6321 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 6322 | } |
michael@0 | 6323 | } |
michael@0 | 6324 | |
michael@0 | 6325 | return nullptr; |
michael@0 | 6326 | } |
michael@0 | 6327 | |
michael@0 | 6328 | NS_IMETHODIMP |
michael@0 | 6329 | nsDocument::LoadBindingDocument(const nsAString& aURI) |
michael@0 | 6330 | { |
michael@0 | 6331 | ErrorResult rv; |
michael@0 | 6332 | nsIDocument::LoadBindingDocument(aURI, rv); |
michael@0 | 6333 | return rv.ErrorCode(); |
michael@0 | 6334 | } |
michael@0 | 6335 | |
michael@0 | 6336 | void |
michael@0 | 6337 | nsIDocument::LoadBindingDocument(const nsAString& aURI, ErrorResult& rv) |
michael@0 | 6338 | { |
michael@0 | 6339 | nsCOMPtr<nsIURI> uri; |
michael@0 | 6340 | rv = NS_NewURI(getter_AddRefs(uri), aURI, |
michael@0 | 6341 | mCharacterSet.get(), |
michael@0 | 6342 | GetDocBaseURI()); |
michael@0 | 6343 | if (rv.Failed()) { |
michael@0 | 6344 | return; |
michael@0 | 6345 | } |
michael@0 | 6346 | |
michael@0 | 6347 | // Figure out the right principal to use |
michael@0 | 6348 | nsCOMPtr<nsIPrincipal> subject; |
michael@0 | 6349 | nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
michael@0 | 6350 | if (secMan) { |
michael@0 | 6351 | rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject)); |
michael@0 | 6352 | if (rv.Failed()) { |
michael@0 | 6353 | return; |
michael@0 | 6354 | } |
michael@0 | 6355 | } |
michael@0 | 6356 | |
michael@0 | 6357 | if (!subject) { |
michael@0 | 6358 | // Fall back to our principal. Or should we fall back to the null |
michael@0 | 6359 | // principal? The latter would just mean no binding loads.... |
michael@0 | 6360 | subject = NodePrincipal(); |
michael@0 | 6361 | } |
michael@0 | 6362 | |
michael@0 | 6363 | BindingManager()->LoadBindingDocument(this, uri, subject); |
michael@0 | 6364 | } |
michael@0 | 6365 | |
michael@0 | 6366 | NS_IMETHODIMP |
michael@0 | 6367 | nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult) |
michael@0 | 6368 | { |
michael@0 | 6369 | nsCOMPtr<nsINode> node = do_QueryInterface(aNode); |
michael@0 | 6370 | NS_ENSURE_ARG_POINTER(node); |
michael@0 | 6371 | |
michael@0 | 6372 | Element* bindingParent = nsIDocument::GetBindingParent(*node); |
michael@0 | 6373 | nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent); |
michael@0 | 6374 | retval.forget(aResult); |
michael@0 | 6375 | return NS_OK; |
michael@0 | 6376 | } |
michael@0 | 6377 | |
michael@0 | 6378 | Element* |
michael@0 | 6379 | nsIDocument::GetBindingParent(nsINode& aNode) |
michael@0 | 6380 | { |
michael@0 | 6381 | nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode)); |
michael@0 | 6382 | if (!content) |
michael@0 | 6383 | return nullptr; |
michael@0 | 6384 | |
michael@0 | 6385 | nsIContent* bindingParent = content->GetBindingParent(); |
michael@0 | 6386 | return bindingParent ? bindingParent->AsElement() : nullptr; |
michael@0 | 6387 | } |
michael@0 | 6388 | |
michael@0 | 6389 | static Element* |
michael@0 | 6390 | GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName, |
michael@0 | 6391 | const nsAString& aAttrValue, bool aUniversalMatch) |
michael@0 | 6392 | { |
michael@0 | 6393 | if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) : |
michael@0 | 6394 | aContent->AttrValueIs(kNameSpaceID_None, aAttrName, |
michael@0 | 6395 | aAttrValue, eCaseMatters)) { |
michael@0 | 6396 | return aContent->AsElement(); |
michael@0 | 6397 | } |
michael@0 | 6398 | |
michael@0 | 6399 | for (nsIContent* child = aContent->GetFirstChild(); |
michael@0 | 6400 | child; |
michael@0 | 6401 | child = child->GetNextSibling()) { |
michael@0 | 6402 | |
michael@0 | 6403 | Element* matchedElement = |
michael@0 | 6404 | GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch); |
michael@0 | 6405 | if (matchedElement) |
michael@0 | 6406 | return matchedElement; |
michael@0 | 6407 | } |
michael@0 | 6408 | |
michael@0 | 6409 | return nullptr; |
michael@0 | 6410 | } |
michael@0 | 6411 | |
michael@0 | 6412 | Element* |
michael@0 | 6413 | nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement, |
michael@0 | 6414 | nsIAtom* aAttrName, |
michael@0 | 6415 | const nsAString& aAttrValue) const |
michael@0 | 6416 | { |
michael@0 | 6417 | nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement); |
michael@0 | 6418 | if (!nodeList) |
michael@0 | 6419 | return nullptr; |
michael@0 | 6420 | |
michael@0 | 6421 | uint32_t length = 0; |
michael@0 | 6422 | nodeList->GetLength(&length); |
michael@0 | 6423 | |
michael@0 | 6424 | bool universalMatch = aAttrValue.EqualsLiteral("*"); |
michael@0 | 6425 | |
michael@0 | 6426 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 6427 | nsIContent* current = nodeList->Item(i); |
michael@0 | 6428 | Element* matchedElm = |
michael@0 | 6429 | GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch); |
michael@0 | 6430 | if (matchedElm) |
michael@0 | 6431 | return matchedElm; |
michael@0 | 6432 | } |
michael@0 | 6433 | |
michael@0 | 6434 | return nullptr; |
michael@0 | 6435 | } |
michael@0 | 6436 | |
michael@0 | 6437 | NS_IMETHODIMP |
michael@0 | 6438 | nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement, |
michael@0 | 6439 | const nsAString& aAttrName, |
michael@0 | 6440 | const nsAString& aAttrValue, |
michael@0 | 6441 | nsIDOMElement** aResult) |
michael@0 | 6442 | { |
michael@0 | 6443 | nsCOMPtr<Element> element = do_QueryInterface(aElement); |
michael@0 | 6444 | NS_ENSURE_ARG_POINTER(element); |
michael@0 | 6445 | |
michael@0 | 6446 | Element* anonEl = |
michael@0 | 6447 | nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName, |
michael@0 | 6448 | aAttrValue); |
michael@0 | 6449 | nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl); |
michael@0 | 6450 | retval.forget(aResult); |
michael@0 | 6451 | return NS_OK; |
michael@0 | 6452 | } |
michael@0 | 6453 | |
michael@0 | 6454 | Element* |
michael@0 | 6455 | nsIDocument::GetAnonymousElementByAttribute(Element& aElement, |
michael@0 | 6456 | const nsAString& aAttrName, |
michael@0 | 6457 | const nsAString& aAttrValue) |
michael@0 | 6458 | { |
michael@0 | 6459 | nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName); |
michael@0 | 6460 | |
michael@0 | 6461 | return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue); |
michael@0 | 6462 | } |
michael@0 | 6463 | |
michael@0 | 6464 | |
michael@0 | 6465 | NS_IMETHODIMP |
michael@0 | 6466 | nsDocument::GetAnonymousNodes(nsIDOMElement* aElement, |
michael@0 | 6467 | nsIDOMNodeList** aResult) |
michael@0 | 6468 | { |
michael@0 | 6469 | *aResult = nullptr; |
michael@0 | 6470 | |
michael@0 | 6471 | nsCOMPtr<nsIContent> content(do_QueryInterface(aElement)); |
michael@0 | 6472 | return BindingManager()->GetAnonymousNodesFor(content, aResult); |
michael@0 | 6473 | } |
michael@0 | 6474 | |
michael@0 | 6475 | nsINodeList* |
michael@0 | 6476 | nsIDocument::GetAnonymousNodes(Element& aElement) |
michael@0 | 6477 | { |
michael@0 | 6478 | return BindingManager()->GetAnonymousNodesFor(&aElement); |
michael@0 | 6479 | } |
michael@0 | 6480 | |
michael@0 | 6481 | NS_IMETHODIMP |
michael@0 | 6482 | nsDocument::CreateRange(nsIDOMRange** aReturn) |
michael@0 | 6483 | { |
michael@0 | 6484 | ErrorResult rv; |
michael@0 | 6485 | *aReturn = nsIDocument::CreateRange(rv).take(); |
michael@0 | 6486 | return rv.ErrorCode(); |
michael@0 | 6487 | } |
michael@0 | 6488 | |
michael@0 | 6489 | already_AddRefed<nsRange> |
michael@0 | 6490 | nsIDocument::CreateRange(ErrorResult& rv) |
michael@0 | 6491 | { |
michael@0 | 6492 | nsRefPtr<nsRange> range = new nsRange(this); |
michael@0 | 6493 | nsresult res = range->Set(this, 0, this, 0); |
michael@0 | 6494 | if (NS_FAILED(res)) { |
michael@0 | 6495 | rv.Throw(res); |
michael@0 | 6496 | return nullptr; |
michael@0 | 6497 | } |
michael@0 | 6498 | |
michael@0 | 6499 | return range.forget(); |
michael@0 | 6500 | } |
michael@0 | 6501 | |
michael@0 | 6502 | NS_IMETHODIMP |
michael@0 | 6503 | nsDocument::CreateNodeIterator(nsIDOMNode *aRoot, |
michael@0 | 6504 | uint32_t aWhatToShow, |
michael@0 | 6505 | nsIDOMNodeFilter *aFilter, |
michael@0 | 6506 | uint8_t aOptionalArgc, |
michael@0 | 6507 | nsIDOMNodeIterator **_retval) |
michael@0 | 6508 | { |
michael@0 | 6509 | *_retval = nullptr; |
michael@0 | 6510 | |
michael@0 | 6511 | if (!aOptionalArgc) { |
michael@0 | 6512 | aWhatToShow = nsIDOMNodeFilter::SHOW_ALL; |
michael@0 | 6513 | } |
michael@0 | 6514 | |
michael@0 | 6515 | if (!aRoot) { |
michael@0 | 6516 | return NS_ERROR_DOM_NOT_SUPPORTED_ERR; |
michael@0 | 6517 | } |
michael@0 | 6518 | |
michael@0 | 6519 | nsCOMPtr<nsINode> root = do_QueryInterface(aRoot); |
michael@0 | 6520 | NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED); |
michael@0 | 6521 | |
michael@0 | 6522 | ErrorResult rv; |
michael@0 | 6523 | NodeFilterHolder holder(aFilter); |
michael@0 | 6524 | *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow, holder, |
michael@0 | 6525 | rv).take(); |
michael@0 | 6526 | return rv.ErrorCode(); |
michael@0 | 6527 | } |
michael@0 | 6528 | |
michael@0 | 6529 | already_AddRefed<NodeIterator> |
michael@0 | 6530 | nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow, |
michael@0 | 6531 | NodeFilter* aFilter, |
michael@0 | 6532 | ErrorResult& rv) const |
michael@0 | 6533 | { |
michael@0 | 6534 | NodeFilterHolder holder(aFilter); |
michael@0 | 6535 | return CreateNodeIterator(aRoot, aWhatToShow, holder, rv); |
michael@0 | 6536 | } |
michael@0 | 6537 | |
michael@0 | 6538 | already_AddRefed<NodeIterator> |
michael@0 | 6539 | nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow, |
michael@0 | 6540 | const NodeFilterHolder& aFilter, |
michael@0 | 6541 | ErrorResult& rv) const |
michael@0 | 6542 | { |
michael@0 | 6543 | nsINode* root = &aRoot; |
michael@0 | 6544 | nsRefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow, |
michael@0 | 6545 | aFilter); |
michael@0 | 6546 | return iterator.forget(); |
michael@0 | 6547 | } |
michael@0 | 6548 | |
michael@0 | 6549 | NS_IMETHODIMP |
michael@0 | 6550 | nsDocument::CreateTreeWalker(nsIDOMNode *aRoot, |
michael@0 | 6551 | uint32_t aWhatToShow, |
michael@0 | 6552 | nsIDOMNodeFilter *aFilter, |
michael@0 | 6553 | uint8_t aOptionalArgc, |
michael@0 | 6554 | nsIDOMTreeWalker **_retval) |
michael@0 | 6555 | { |
michael@0 | 6556 | *_retval = nullptr; |
michael@0 | 6557 | |
michael@0 | 6558 | if (!aOptionalArgc) { |
michael@0 | 6559 | aWhatToShow = nsIDOMNodeFilter::SHOW_ALL; |
michael@0 | 6560 | } |
michael@0 | 6561 | |
michael@0 | 6562 | nsCOMPtr<nsINode> root = do_QueryInterface(aRoot); |
michael@0 | 6563 | NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 6564 | |
michael@0 | 6565 | ErrorResult rv; |
michael@0 | 6566 | NodeFilterHolder holder(aFilter); |
michael@0 | 6567 | *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow, holder, |
michael@0 | 6568 | rv).take(); |
michael@0 | 6569 | return rv.ErrorCode(); |
michael@0 | 6570 | } |
michael@0 | 6571 | |
michael@0 | 6572 | already_AddRefed<TreeWalker> |
michael@0 | 6573 | nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow, |
michael@0 | 6574 | NodeFilter* aFilter, |
michael@0 | 6575 | ErrorResult& rv) const |
michael@0 | 6576 | { |
michael@0 | 6577 | NodeFilterHolder holder(aFilter); |
michael@0 | 6578 | return CreateTreeWalker(aRoot, aWhatToShow, holder, rv); |
michael@0 | 6579 | } |
michael@0 | 6580 | |
michael@0 | 6581 | already_AddRefed<TreeWalker> |
michael@0 | 6582 | nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow, |
michael@0 | 6583 | const NodeFilterHolder& aFilter, |
michael@0 | 6584 | ErrorResult& rv) const |
michael@0 | 6585 | { |
michael@0 | 6586 | nsINode* root = &aRoot; |
michael@0 | 6587 | nsRefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, aFilter); |
michael@0 | 6588 | return walker.forget(); |
michael@0 | 6589 | } |
michael@0 | 6590 | |
michael@0 | 6591 | |
michael@0 | 6592 | NS_IMETHODIMP |
michael@0 | 6593 | nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView) |
michael@0 | 6594 | { |
michael@0 | 6595 | *aDefaultView = nullptr; |
michael@0 | 6596 | nsCOMPtr<nsPIDOMWindow> win = GetWindow(); |
michael@0 | 6597 | win.forget(aDefaultView); |
michael@0 | 6598 | return NS_OK; |
michael@0 | 6599 | } |
michael@0 | 6600 | |
michael@0 | 6601 | NS_IMETHODIMP |
michael@0 | 6602 | nsDocument::GetLocation(nsIDOMLocation **_retval) |
michael@0 | 6603 | { |
michael@0 | 6604 | *_retval = nsIDocument::GetLocation().take(); |
michael@0 | 6605 | return NS_OK; |
michael@0 | 6606 | } |
michael@0 | 6607 | |
michael@0 | 6608 | already_AddRefed<nsIDOMLocation> |
michael@0 | 6609 | nsIDocument::GetLocation() const |
michael@0 | 6610 | { |
michael@0 | 6611 | nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject); |
michael@0 | 6612 | |
michael@0 | 6613 | if (!w) { |
michael@0 | 6614 | return nullptr; |
michael@0 | 6615 | } |
michael@0 | 6616 | |
michael@0 | 6617 | nsCOMPtr<nsIDOMLocation> loc; |
michael@0 | 6618 | w->GetLocation(getter_AddRefs(loc)); |
michael@0 | 6619 | return loc.forget(); |
michael@0 | 6620 | } |
michael@0 | 6621 | |
michael@0 | 6622 | Element* |
michael@0 | 6623 | nsIDocument::GetHtmlElement() const |
michael@0 | 6624 | { |
michael@0 | 6625 | Element* rootElement = GetRootElement(); |
michael@0 | 6626 | if (rootElement && rootElement->IsHTML(nsGkAtoms::html)) |
michael@0 | 6627 | return rootElement; |
michael@0 | 6628 | return nullptr; |
michael@0 | 6629 | } |
michael@0 | 6630 | |
michael@0 | 6631 | Element* |
michael@0 | 6632 | nsIDocument::GetHtmlChildElement(nsIAtom* aTag) |
michael@0 | 6633 | { |
michael@0 | 6634 | Element* html = GetHtmlElement(); |
michael@0 | 6635 | if (!html) |
michael@0 | 6636 | return nullptr; |
michael@0 | 6637 | |
michael@0 | 6638 | // Look for the element with aTag inside html. This needs to run |
michael@0 | 6639 | // forwards to find the first such element. |
michael@0 | 6640 | for (nsIContent* child = html->GetFirstChild(); |
michael@0 | 6641 | child; |
michael@0 | 6642 | child = child->GetNextSibling()) { |
michael@0 | 6643 | if (child->IsHTML(aTag)) |
michael@0 | 6644 | return child->AsElement(); |
michael@0 | 6645 | } |
michael@0 | 6646 | return nullptr; |
michael@0 | 6647 | } |
michael@0 | 6648 | |
michael@0 | 6649 | nsIContent* |
michael@0 | 6650 | nsDocument::GetTitleContent(uint32_t aNamespace) |
michael@0 | 6651 | { |
michael@0 | 6652 | // mMayHaveTitleElement will have been set to true if any HTML or SVG |
michael@0 | 6653 | // <title> element has been bound to this document. So if it's false, |
michael@0 | 6654 | // we know there is nothing to do here. This avoids us having to search |
michael@0 | 6655 | // the whole DOM if someone calls document.title on a large document |
michael@0 | 6656 | // without a title. |
michael@0 | 6657 | if (!mMayHaveTitleElement) |
michael@0 | 6658 | return nullptr; |
michael@0 | 6659 | |
michael@0 | 6660 | nsRefPtr<nsContentList> list = |
michael@0 | 6661 | NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title")); |
michael@0 | 6662 | |
michael@0 | 6663 | return list->Item(0, false); |
michael@0 | 6664 | } |
michael@0 | 6665 | |
michael@0 | 6666 | void |
michael@0 | 6667 | nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle) |
michael@0 | 6668 | { |
michael@0 | 6669 | nsIContent* title = GetTitleContent(aNamespace); |
michael@0 | 6670 | if (!title) |
michael@0 | 6671 | return; |
michael@0 | 6672 | if(!nsContentUtils::GetNodeTextContent(title, false, aTitle)) |
michael@0 | 6673 | NS_RUNTIMEABORT("OOM"); |
michael@0 | 6674 | } |
michael@0 | 6675 | |
michael@0 | 6676 | NS_IMETHODIMP |
michael@0 | 6677 | nsDocument::GetTitle(nsAString& aTitle) |
michael@0 | 6678 | { |
michael@0 | 6679 | nsString title; |
michael@0 | 6680 | GetTitle(title); |
michael@0 | 6681 | aTitle = title; |
michael@0 | 6682 | return NS_OK; |
michael@0 | 6683 | } |
michael@0 | 6684 | |
michael@0 | 6685 | void |
michael@0 | 6686 | nsDocument::GetTitle(nsString& aTitle) |
michael@0 | 6687 | { |
michael@0 | 6688 | aTitle.Truncate(); |
michael@0 | 6689 | |
michael@0 | 6690 | nsIContent *rootElement = GetRootElement(); |
michael@0 | 6691 | if (!rootElement) |
michael@0 | 6692 | return; |
michael@0 | 6693 | |
michael@0 | 6694 | nsAutoString tmp; |
michael@0 | 6695 | |
michael@0 | 6696 | switch (rootElement->GetNameSpaceID()) { |
michael@0 | 6697 | #ifdef MOZ_XUL |
michael@0 | 6698 | case kNameSpaceID_XUL: |
michael@0 | 6699 | rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp); |
michael@0 | 6700 | break; |
michael@0 | 6701 | #endif |
michael@0 | 6702 | case kNameSpaceID_SVG: |
michael@0 | 6703 | if (rootElement->Tag() == nsGkAtoms::svg) { |
michael@0 | 6704 | GetTitleFromElement(kNameSpaceID_SVG, tmp); |
michael@0 | 6705 | break; |
michael@0 | 6706 | } // else fall through |
michael@0 | 6707 | default: |
michael@0 | 6708 | GetTitleFromElement(kNameSpaceID_XHTML, tmp); |
michael@0 | 6709 | break; |
michael@0 | 6710 | } |
michael@0 | 6711 | |
michael@0 | 6712 | tmp.CompressWhitespace(); |
michael@0 | 6713 | aTitle = tmp; |
michael@0 | 6714 | } |
michael@0 | 6715 | |
michael@0 | 6716 | NS_IMETHODIMP |
michael@0 | 6717 | nsDocument::SetTitle(const nsAString& aTitle) |
michael@0 | 6718 | { |
michael@0 | 6719 | Element *rootElement = GetRootElement(); |
michael@0 | 6720 | if (!rootElement) |
michael@0 | 6721 | return NS_OK; |
michael@0 | 6722 | |
michael@0 | 6723 | switch (rootElement->GetNameSpaceID()) { |
michael@0 | 6724 | case kNameSpaceID_SVG: |
michael@0 | 6725 | return NS_OK; // SVG doesn't support setting a title |
michael@0 | 6726 | #ifdef MOZ_XUL |
michael@0 | 6727 | case kNameSpaceID_XUL: |
michael@0 | 6728 | return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title, |
michael@0 | 6729 | aTitle, true); |
michael@0 | 6730 | #endif |
michael@0 | 6731 | } |
michael@0 | 6732 | |
michael@0 | 6733 | // Batch updates so that mutation events don't change "the title |
michael@0 | 6734 | // element" under us |
michael@0 | 6735 | mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true); |
michael@0 | 6736 | |
michael@0 | 6737 | nsIContent* title = GetTitleContent(kNameSpaceID_XHTML); |
michael@0 | 6738 | if (!title) { |
michael@0 | 6739 | Element *head = GetHeadElement(); |
michael@0 | 6740 | if (!head) |
michael@0 | 6741 | return NS_OK; |
michael@0 | 6742 | |
michael@0 | 6743 | { |
michael@0 | 6744 | nsCOMPtr<nsINodeInfo> titleInfo; |
michael@0 | 6745 | titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr, |
michael@0 | 6746 | kNameSpaceID_XHTML, |
michael@0 | 6747 | nsIDOMNode::ELEMENT_NODE); |
michael@0 | 6748 | title = NS_NewHTMLTitleElement(titleInfo.forget()); |
michael@0 | 6749 | if (!title) |
michael@0 | 6750 | return NS_OK; |
michael@0 | 6751 | } |
michael@0 | 6752 | |
michael@0 | 6753 | head->AppendChildTo(title, true); |
michael@0 | 6754 | } |
michael@0 | 6755 | |
michael@0 | 6756 | return nsContentUtils::SetNodeTextContent(title, aTitle, false); |
michael@0 | 6757 | } |
michael@0 | 6758 | |
michael@0 | 6759 | void |
michael@0 | 6760 | nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv) |
michael@0 | 6761 | { |
michael@0 | 6762 | rv = SetTitle(aTitle); |
michael@0 | 6763 | } |
michael@0 | 6764 | |
michael@0 | 6765 | void |
michael@0 | 6766 | nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement) |
michael@0 | 6767 | { |
michael@0 | 6768 | NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement, |
michael@0 | 6769 | "Setting a title while unlinking or destroying the element?"); |
michael@0 | 6770 | if (mInUnlinkOrDeletion) { |
michael@0 | 6771 | return; |
michael@0 | 6772 | } |
michael@0 | 6773 | |
michael@0 | 6774 | if (aBoundTitleElement) { |
michael@0 | 6775 | mMayHaveTitleElement = true; |
michael@0 | 6776 | } |
michael@0 | 6777 | if (mPendingTitleChangeEvent.IsPending()) |
michael@0 | 6778 | return; |
michael@0 | 6779 | |
michael@0 | 6780 | nsRefPtr<nsRunnableMethod<nsDocument, void, false> > event = |
michael@0 | 6781 | NS_NewNonOwningRunnableMethod(this, |
michael@0 | 6782 | &nsDocument::DoNotifyPossibleTitleChange); |
michael@0 | 6783 | nsresult rv = NS_DispatchToCurrentThread(event); |
michael@0 | 6784 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 6785 | mPendingTitleChangeEvent = event; |
michael@0 | 6786 | } |
michael@0 | 6787 | } |
michael@0 | 6788 | |
michael@0 | 6789 | void |
michael@0 | 6790 | nsDocument::DoNotifyPossibleTitleChange() |
michael@0 | 6791 | { |
michael@0 | 6792 | mPendingTitleChangeEvent.Forget(); |
michael@0 | 6793 | mHaveFiredTitleChange = true; |
michael@0 | 6794 | |
michael@0 | 6795 | nsAutoString title; |
michael@0 | 6796 | GetTitle(title); |
michael@0 | 6797 | |
michael@0 | 6798 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 6799 | if (shell) { |
michael@0 | 6800 | nsCOMPtr<nsISupports> container = |
michael@0 | 6801 | shell->GetPresContext()->GetContainerWeak(); |
michael@0 | 6802 | if (container) { |
michael@0 | 6803 | nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container); |
michael@0 | 6804 | if (docShellWin) { |
michael@0 | 6805 | docShellWin->SetTitle(title.get()); |
michael@0 | 6806 | } |
michael@0 | 6807 | } |
michael@0 | 6808 | } |
michael@0 | 6809 | |
michael@0 | 6810 | // Fire a DOM event for the title change. |
michael@0 | 6811 | nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this), |
michael@0 | 6812 | NS_LITERAL_STRING("DOMTitleChanged"), |
michael@0 | 6813 | true, true); |
michael@0 | 6814 | } |
michael@0 | 6815 | |
michael@0 | 6816 | already_AddRefed<nsIBoxObject> |
michael@0 | 6817 | nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv) |
michael@0 | 6818 | { |
michael@0 | 6819 | if (!aElement) { |
michael@0 | 6820 | aRv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 6821 | return nullptr; |
michael@0 | 6822 | } |
michael@0 | 6823 | |
michael@0 | 6824 | nsIDocument* doc = aElement->OwnerDoc(); |
michael@0 | 6825 | if (doc != this) { |
michael@0 | 6826 | aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
michael@0 | 6827 | return nullptr; |
michael@0 | 6828 | } |
michael@0 | 6829 | |
michael@0 | 6830 | if (!mHasWarnedAboutBoxObjects && !aElement->IsXUL()) { |
michael@0 | 6831 | mHasWarnedAboutBoxObjects = true; |
michael@0 | 6832 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 6833 | NS_LITERAL_CSTRING("BoxObjects"), this, |
michael@0 | 6834 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 6835 | "UseOfGetBoxObjectForWarning"); |
michael@0 | 6836 | } |
michael@0 | 6837 | |
michael@0 | 6838 | if (!mBoxObjectTable) { |
michael@0 | 6839 | mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(12); |
michael@0 | 6840 | } else { |
michael@0 | 6841 | nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement); |
michael@0 | 6842 | if (boxObject) { |
michael@0 | 6843 | return boxObject.forget(); |
michael@0 | 6844 | } |
michael@0 | 6845 | } |
michael@0 | 6846 | |
michael@0 | 6847 | int32_t namespaceID; |
michael@0 | 6848 | nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID); |
michael@0 | 6849 | |
michael@0 | 6850 | nsAutoCString contractID("@mozilla.org/layout/xul-boxobject"); |
michael@0 | 6851 | if (namespaceID == kNameSpaceID_XUL) { |
michael@0 | 6852 | if (tag == nsGkAtoms::browser || |
michael@0 | 6853 | tag == nsGkAtoms::editor || |
michael@0 | 6854 | tag == nsGkAtoms::iframe) |
michael@0 | 6855 | contractID += "-container"; |
michael@0 | 6856 | else if (tag == nsGkAtoms::menu) |
michael@0 | 6857 | contractID += "-menu"; |
michael@0 | 6858 | else if (tag == nsGkAtoms::popup || |
michael@0 | 6859 | tag == nsGkAtoms::menupopup || |
michael@0 | 6860 | tag == nsGkAtoms::panel || |
michael@0 | 6861 | tag == nsGkAtoms::tooltip) |
michael@0 | 6862 | contractID += "-popup"; |
michael@0 | 6863 | else if (tag == nsGkAtoms::tree) |
michael@0 | 6864 | contractID += "-tree"; |
michael@0 | 6865 | else if (tag == nsGkAtoms::listbox) |
michael@0 | 6866 | contractID += "-listbox"; |
michael@0 | 6867 | else if (tag == nsGkAtoms::scrollbox) |
michael@0 | 6868 | contractID += "-scrollbox"; |
michael@0 | 6869 | } |
michael@0 | 6870 | contractID += ";1"; |
michael@0 | 6871 | |
michael@0 | 6872 | nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get())); |
michael@0 | 6873 | if (!boxObject) { |
michael@0 | 6874 | aRv.Throw(NS_ERROR_FAILURE); |
michael@0 | 6875 | return nullptr; |
michael@0 | 6876 | } |
michael@0 | 6877 | |
michael@0 | 6878 | boxObject->Init(aElement); |
michael@0 | 6879 | |
michael@0 | 6880 | if (mBoxObjectTable) { |
michael@0 | 6881 | mBoxObjectTable->Put(aElement, boxObject.get()); |
michael@0 | 6882 | } |
michael@0 | 6883 | |
michael@0 | 6884 | return boxObject.forget(); |
michael@0 | 6885 | } |
michael@0 | 6886 | |
michael@0 | 6887 | void |
michael@0 | 6888 | nsDocument::ClearBoxObjectFor(nsIContent* aContent) |
michael@0 | 6889 | { |
michael@0 | 6890 | if (mBoxObjectTable) { |
michael@0 | 6891 | nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent); |
michael@0 | 6892 | if (boxObject) { |
michael@0 | 6893 | boxObject->Clear(); |
michael@0 | 6894 | mBoxObjectTable->Remove(aContent); |
michael@0 | 6895 | } |
michael@0 | 6896 | } |
michael@0 | 6897 | } |
michael@0 | 6898 | |
michael@0 | 6899 | void |
michael@0 | 6900 | nsDocument::FlushSkinBindings() |
michael@0 | 6901 | { |
michael@0 | 6902 | BindingManager()->FlushSkinBindings(); |
michael@0 | 6903 | } |
michael@0 | 6904 | |
michael@0 | 6905 | nsresult |
michael@0 | 6906 | nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader) |
michael@0 | 6907 | { |
michael@0 | 6908 | mInitializableFrameLoaders.RemoveElement(aLoader); |
michael@0 | 6909 | // Don't even try to initialize. |
michael@0 | 6910 | if (mInDestructor) { |
michael@0 | 6911 | NS_WARNING("Trying to initialize a frame loader while" |
michael@0 | 6912 | "document is being deleted"); |
michael@0 | 6913 | return NS_ERROR_FAILURE; |
michael@0 | 6914 | } |
michael@0 | 6915 | |
michael@0 | 6916 | mInitializableFrameLoaders.AppendElement(aLoader); |
michael@0 | 6917 | if (!mFrameLoaderRunner) { |
michael@0 | 6918 | mFrameLoaderRunner = |
michael@0 | 6919 | NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); |
michael@0 | 6920 | NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 6921 | nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
michael@0 | 6922 | } |
michael@0 | 6923 | return NS_OK; |
michael@0 | 6924 | } |
michael@0 | 6925 | |
michael@0 | 6926 | nsresult |
michael@0 | 6927 | nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader) |
michael@0 | 6928 | { |
michael@0 | 6929 | mInitializableFrameLoaders.RemoveElement(aLoader); |
michael@0 | 6930 | if (mInDestructor) { |
michael@0 | 6931 | return NS_ERROR_FAILURE; |
michael@0 | 6932 | } |
michael@0 | 6933 | |
michael@0 | 6934 | mFinalizableFrameLoaders.AppendElement(aLoader); |
michael@0 | 6935 | if (!mFrameLoaderRunner) { |
michael@0 | 6936 | mFrameLoaderRunner = |
michael@0 | 6937 | NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); |
michael@0 | 6938 | NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 6939 | nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
michael@0 | 6940 | } |
michael@0 | 6941 | return NS_OK; |
michael@0 | 6942 | } |
michael@0 | 6943 | |
michael@0 | 6944 | void |
michael@0 | 6945 | nsDocument::MaybeInitializeFinalizeFrameLoaders() |
michael@0 | 6946 | { |
michael@0 | 6947 | if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) { |
michael@0 | 6948 | // This method will be recalled when mUpdateNestLevel drops to 0, |
michael@0 | 6949 | // or when !mDelayFrameLoaderInitialization. |
michael@0 | 6950 | mFrameLoaderRunner = nullptr; |
michael@0 | 6951 | return; |
michael@0 | 6952 | } |
michael@0 | 6953 | |
michael@0 | 6954 | // We're not in an update, but it is not safe to run scripts, so |
michael@0 | 6955 | // postpone frameloader initialization and finalization. |
michael@0 | 6956 | if (!nsContentUtils::IsSafeToRunScript()) { |
michael@0 | 6957 | if (!mInDestructor && !mFrameLoaderRunner && |
michael@0 | 6958 | (mInitializableFrameLoaders.Length() || |
michael@0 | 6959 | mFinalizableFrameLoaders.Length())) { |
michael@0 | 6960 | mFrameLoaderRunner = |
michael@0 | 6961 | NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders); |
michael@0 | 6962 | nsContentUtils::AddScriptRunner(mFrameLoaderRunner); |
michael@0 | 6963 | } |
michael@0 | 6964 | return; |
michael@0 | 6965 | } |
michael@0 | 6966 | mFrameLoaderRunner = nullptr; |
michael@0 | 6967 | |
michael@0 | 6968 | // Don't use a temporary array for mInitializableFrameLoaders, because |
michael@0 | 6969 | // loading a frame may cause some other frameloader to be removed from the |
michael@0 | 6970 | // array. But be careful to keep the loader alive when starting the load! |
michael@0 | 6971 | while (mInitializableFrameLoaders.Length()) { |
michael@0 | 6972 | nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0]; |
michael@0 | 6973 | mInitializableFrameLoaders.RemoveElementAt(0); |
michael@0 | 6974 | NS_ASSERTION(loader, "null frameloader in the array?"); |
michael@0 | 6975 | loader->ReallyStartLoading(); |
michael@0 | 6976 | } |
michael@0 | 6977 | |
michael@0 | 6978 | uint32_t length = mFinalizableFrameLoaders.Length(); |
michael@0 | 6979 | if (length > 0) { |
michael@0 | 6980 | nsTArray<nsRefPtr<nsFrameLoader> > loaders; |
michael@0 | 6981 | mFinalizableFrameLoaders.SwapElements(loaders); |
michael@0 | 6982 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 6983 | loaders[i]->Finalize(); |
michael@0 | 6984 | } |
michael@0 | 6985 | } |
michael@0 | 6986 | } |
michael@0 | 6987 | |
michael@0 | 6988 | void |
michael@0 | 6989 | nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell) |
michael@0 | 6990 | { |
michael@0 | 6991 | uint32_t length = mInitializableFrameLoaders.Length(); |
michael@0 | 6992 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 6993 | if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) { |
michael@0 | 6994 | mInitializableFrameLoaders.RemoveElementAt(i); |
michael@0 | 6995 | return; |
michael@0 | 6996 | } |
michael@0 | 6997 | } |
michael@0 | 6998 | } |
michael@0 | 6999 | |
michael@0 | 7000 | bool |
michael@0 | 7001 | nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) |
michael@0 | 7002 | { |
michael@0 | 7003 | if (aShell) { |
michael@0 | 7004 | uint32_t length = mFinalizableFrameLoaders.Length(); |
michael@0 | 7005 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 7006 | if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) { |
michael@0 | 7007 | return true; |
michael@0 | 7008 | } |
michael@0 | 7009 | } |
michael@0 | 7010 | } |
michael@0 | 7011 | return false; |
michael@0 | 7012 | } |
michael@0 | 7013 | |
michael@0 | 7014 | nsIDocument* |
michael@0 | 7015 | nsDocument::RequestExternalResource(nsIURI* aURI, |
michael@0 | 7016 | nsINode* aRequestingNode, |
michael@0 | 7017 | ExternalResourceLoad** aPendingLoad) |
michael@0 | 7018 | { |
michael@0 | 7019 | NS_PRECONDITION(aURI, "Must have a URI"); |
michael@0 | 7020 | NS_PRECONDITION(aRequestingNode, "Must have a node"); |
michael@0 | 7021 | if (mDisplayDocument) { |
michael@0 | 7022 | return mDisplayDocument->RequestExternalResource(aURI, |
michael@0 | 7023 | aRequestingNode, |
michael@0 | 7024 | aPendingLoad); |
michael@0 | 7025 | } |
michael@0 | 7026 | |
michael@0 | 7027 | return mExternalResourceMap.RequestResource(aURI, aRequestingNode, |
michael@0 | 7028 | this, aPendingLoad); |
michael@0 | 7029 | } |
michael@0 | 7030 | |
michael@0 | 7031 | void |
michael@0 | 7032 | nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData) |
michael@0 | 7033 | { |
michael@0 | 7034 | mExternalResourceMap.EnumerateResources(aCallback, aData); |
michael@0 | 7035 | } |
michael@0 | 7036 | |
michael@0 | 7037 | nsSMILAnimationController* |
michael@0 | 7038 | nsDocument::GetAnimationController() |
michael@0 | 7039 | { |
michael@0 | 7040 | // We create the animation controller lazily because most documents won't want |
michael@0 | 7041 | // one and only SVG documents and the like will call this |
michael@0 | 7042 | if (mAnimationController) |
michael@0 | 7043 | return mAnimationController; |
michael@0 | 7044 | // Refuse to create an Animation Controller for data documents. |
michael@0 | 7045 | if (mLoadedAsData || mLoadedAsInteractiveData) |
michael@0 | 7046 | return nullptr; |
michael@0 | 7047 | |
michael@0 | 7048 | mAnimationController = new nsSMILAnimationController(this); |
michael@0 | 7049 | |
michael@0 | 7050 | // If there's a presContext then check the animation mode and pause if |
michael@0 | 7051 | // necessary. |
michael@0 | 7052 | nsIPresShell *shell = GetShell(); |
michael@0 | 7053 | if (mAnimationController && shell) { |
michael@0 | 7054 | nsPresContext *context = shell->GetPresContext(); |
michael@0 | 7055 | if (context && |
michael@0 | 7056 | context->ImageAnimationMode() == imgIContainer::kDontAnimMode) { |
michael@0 | 7057 | mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF); |
michael@0 | 7058 | } |
michael@0 | 7059 | } |
michael@0 | 7060 | |
michael@0 | 7061 | // If we're hidden (or being hidden), notify the newly-created animation |
michael@0 | 7062 | // controller. (Skip this check for SVG-as-an-image documents, though, |
michael@0 | 7063 | // because they don't get OnPageShow / OnPageHide calls). |
michael@0 | 7064 | if (!mIsShowing && !mIsBeingUsedAsImage) { |
michael@0 | 7065 | mAnimationController->OnPageHide(); |
michael@0 | 7066 | } |
michael@0 | 7067 | |
michael@0 | 7068 | return mAnimationController; |
michael@0 | 7069 | } |
michael@0 | 7070 | |
michael@0 | 7071 | /** |
michael@0 | 7072 | * Retrieve the "direction" property of the document. |
michael@0 | 7073 | * |
michael@0 | 7074 | * @lina 01/09/2001 |
michael@0 | 7075 | */ |
michael@0 | 7076 | NS_IMETHODIMP |
michael@0 | 7077 | nsDocument::GetDir(nsAString& aDirection) |
michael@0 | 7078 | { |
michael@0 | 7079 | nsIDocument::GetDir(aDirection); |
michael@0 | 7080 | return NS_OK; |
michael@0 | 7081 | } |
michael@0 | 7082 | |
michael@0 | 7083 | void |
michael@0 | 7084 | nsIDocument::GetDir(nsAString& aDirection) const |
michael@0 | 7085 | { |
michael@0 | 7086 | aDirection.Truncate(); |
michael@0 | 7087 | Element* rootElement = GetHtmlElement(); |
michael@0 | 7088 | if (rootElement) { |
michael@0 | 7089 | static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection); |
michael@0 | 7090 | } |
michael@0 | 7091 | } |
michael@0 | 7092 | |
michael@0 | 7093 | /** |
michael@0 | 7094 | * Set the "direction" property of the document. |
michael@0 | 7095 | * |
michael@0 | 7096 | * @lina 01/09/2001 |
michael@0 | 7097 | */ |
michael@0 | 7098 | NS_IMETHODIMP |
michael@0 | 7099 | nsDocument::SetDir(const nsAString& aDirection) |
michael@0 | 7100 | { |
michael@0 | 7101 | nsIDocument::SetDir(aDirection); |
michael@0 | 7102 | return NS_OK; |
michael@0 | 7103 | } |
michael@0 | 7104 | |
michael@0 | 7105 | void |
michael@0 | 7106 | nsIDocument::SetDir(const nsAString& aDirection) |
michael@0 | 7107 | { |
michael@0 | 7108 | Element* rootElement = GetHtmlElement(); |
michael@0 | 7109 | if (rootElement) { |
michael@0 | 7110 | rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, |
michael@0 | 7111 | aDirection, true); |
michael@0 | 7112 | } |
michael@0 | 7113 | } |
michael@0 | 7114 | |
michael@0 | 7115 | NS_IMETHODIMP |
michael@0 | 7116 | nsDocument::GetInputEncoding(nsAString& aInputEncoding) |
michael@0 | 7117 | { |
michael@0 | 7118 | nsIDocument::GetInputEncoding(aInputEncoding); |
michael@0 | 7119 | return NS_OK; |
michael@0 | 7120 | } |
michael@0 | 7121 | |
michael@0 | 7122 | void |
michael@0 | 7123 | nsIDocument::GetInputEncoding(nsAString& aInputEncoding) |
michael@0 | 7124 | { |
michael@0 | 7125 | // Not const function, because WarnOnceAbout is not a const method |
michael@0 | 7126 | WarnOnceAbout(eInputEncoding); |
michael@0 | 7127 | if (mHaveInputEncoding) { |
michael@0 | 7128 | return GetCharacterSet(aInputEncoding); |
michael@0 | 7129 | } |
michael@0 | 7130 | |
michael@0 | 7131 | SetDOMStringToNull(aInputEncoding); |
michael@0 | 7132 | } |
michael@0 | 7133 | |
michael@0 | 7134 | NS_IMETHODIMP |
michael@0 | 7135 | nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument) |
michael@0 | 7136 | { |
michael@0 | 7137 | *aSyntheticDocument = mIsSyntheticDocument; |
michael@0 | 7138 | return NS_OK; |
michael@0 | 7139 | } |
michael@0 | 7140 | |
michael@0 | 7141 | NS_IMETHODIMP |
michael@0 | 7142 | nsDocument::GetDocumentURI(nsAString& aDocumentURI) |
michael@0 | 7143 | { |
michael@0 | 7144 | nsString temp; |
michael@0 | 7145 | nsIDocument::GetDocumentURI(temp); |
michael@0 | 7146 | aDocumentURI = temp; |
michael@0 | 7147 | return NS_OK; |
michael@0 | 7148 | } |
michael@0 | 7149 | |
michael@0 | 7150 | void |
michael@0 | 7151 | nsIDocument::GetDocumentURI(nsString& aDocumentURI) const |
michael@0 | 7152 | { |
michael@0 | 7153 | if (mDocumentURI) { |
michael@0 | 7154 | nsAutoCString uri; |
michael@0 | 7155 | mDocumentURI->GetSpec(uri); |
michael@0 | 7156 | CopyUTF8toUTF16(uri, aDocumentURI); |
michael@0 | 7157 | } else { |
michael@0 | 7158 | aDocumentURI.Truncate(); |
michael@0 | 7159 | } |
michael@0 | 7160 | } |
michael@0 | 7161 | |
michael@0 | 7162 | // Alias of above |
michael@0 | 7163 | NS_IMETHODIMP |
michael@0 | 7164 | nsDocument::GetURL(nsAString& aURL) |
michael@0 | 7165 | { |
michael@0 | 7166 | return GetDocumentURI(aURL); |
michael@0 | 7167 | } |
michael@0 | 7168 | |
michael@0 | 7169 | void |
michael@0 | 7170 | nsIDocument::GetURL(nsString& aURL) const |
michael@0 | 7171 | { |
michael@0 | 7172 | return GetDocumentURI(aURL); |
michael@0 | 7173 | } |
michael@0 | 7174 | |
michael@0 | 7175 | void |
michael@0 | 7176 | nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI) const |
michael@0 | 7177 | { |
michael@0 | 7178 | if (!mChromeXHRDocURI || !nsContentUtils::IsCallerChrome()) { |
michael@0 | 7179 | return GetDocumentURI(aDocumentURI); |
michael@0 | 7180 | } |
michael@0 | 7181 | |
michael@0 | 7182 | nsAutoCString uri; |
michael@0 | 7183 | mChromeXHRDocURI->GetSpec(uri); |
michael@0 | 7184 | CopyUTF8toUTF16(uri, aDocumentURI); |
michael@0 | 7185 | } |
michael@0 | 7186 | |
michael@0 | 7187 | nsIURI* |
michael@0 | 7188 | nsIDocument::GetDocumentURIObject() const |
michael@0 | 7189 | { |
michael@0 | 7190 | if (!mChromeXHRDocURI) { |
michael@0 | 7191 | return GetDocumentURI(); |
michael@0 | 7192 | } |
michael@0 | 7193 | |
michael@0 | 7194 | return mChromeXHRDocURI; |
michael@0 | 7195 | } |
michael@0 | 7196 | |
michael@0 | 7197 | |
michael@0 | 7198 | // readonly attribute DOMString compatMode; |
michael@0 | 7199 | // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are |
michael@0 | 7200 | // in almost standards or full standards mode. See bug 105640. This was |
michael@0 | 7201 | // implemented to match MSIE's compatMode property. |
michael@0 | 7202 | NS_IMETHODIMP |
michael@0 | 7203 | nsDocument::GetCompatMode(nsAString& aCompatMode) |
michael@0 | 7204 | { |
michael@0 | 7205 | nsString temp; |
michael@0 | 7206 | nsIDocument::GetCompatMode(temp); |
michael@0 | 7207 | aCompatMode = temp; |
michael@0 | 7208 | return NS_OK; |
michael@0 | 7209 | } |
michael@0 | 7210 | |
michael@0 | 7211 | void |
michael@0 | 7212 | nsIDocument::GetCompatMode(nsString& aCompatMode) const |
michael@0 | 7213 | { |
michael@0 | 7214 | NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks || |
michael@0 | 7215 | mCompatMode == eCompatibility_AlmostStandards || |
michael@0 | 7216 | mCompatMode == eCompatibility_FullStandards, |
michael@0 | 7217 | "mCompatMode is neither quirks nor strict for this document"); |
michael@0 | 7218 | |
michael@0 | 7219 | if (mCompatMode == eCompatibility_NavQuirks) { |
michael@0 | 7220 | aCompatMode.AssignLiteral("BackCompat"); |
michael@0 | 7221 | } else { |
michael@0 | 7222 | aCompatMode.AssignLiteral("CSS1Compat"); |
michael@0 | 7223 | } |
michael@0 | 7224 | } |
michael@0 | 7225 | |
michael@0 | 7226 | static void BlastSubtreeToPieces(nsINode *aNode); |
michael@0 | 7227 | |
michael@0 | 7228 | PLDHashOperator |
michael@0 | 7229 | BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg) |
michael@0 | 7230 | { |
michael@0 | 7231 | nsCOMPtr<nsIAttribute> *attr = |
michael@0 | 7232 | static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg); |
michael@0 | 7233 | |
michael@0 | 7234 | *attr = aData; |
michael@0 | 7235 | |
michael@0 | 7236 | NS_ASSERTION(attr->get(), |
michael@0 | 7237 | "non-nsIAttribute somehow made it into the hashmap?!"); |
michael@0 | 7238 | |
michael@0 | 7239 | return PL_DHASH_STOP; |
michael@0 | 7240 | } |
michael@0 | 7241 | |
michael@0 | 7242 | static void |
michael@0 | 7243 | BlastSubtreeToPieces(nsINode *aNode) |
michael@0 | 7244 | { |
michael@0 | 7245 | if (aNode->IsElement()) { |
michael@0 | 7246 | Element *element = aNode->AsElement(); |
michael@0 | 7247 | const nsDOMAttributeMap *map = element->GetAttributeMap(); |
michael@0 | 7248 | if (map) { |
michael@0 | 7249 | nsCOMPtr<nsIAttribute> attr; |
michael@0 | 7250 | while (map->Enumerate(BlastFunc, &attr) > 0) { |
michael@0 | 7251 | BlastSubtreeToPieces(attr); |
michael@0 | 7252 | |
michael@0 | 7253 | #ifdef DEBUG |
michael@0 | 7254 | nsresult rv = |
michael@0 | 7255 | #endif |
michael@0 | 7256 | element->UnsetAttr(attr->NodeInfo()->NamespaceID(), |
michael@0 | 7257 | attr->NodeInfo()->NameAtom(), |
michael@0 | 7258 | false); |
michael@0 | 7259 | |
michael@0 | 7260 | // XXX Should we abort here? |
michael@0 | 7261 | NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!"); |
michael@0 | 7262 | } |
michael@0 | 7263 | } |
michael@0 | 7264 | } |
michael@0 | 7265 | |
michael@0 | 7266 | uint32_t count = aNode->GetChildCount(); |
michael@0 | 7267 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 7268 | BlastSubtreeToPieces(aNode->GetFirstChild()); |
michael@0 | 7269 | aNode->RemoveChildAt(0, false); |
michael@0 | 7270 | } |
michael@0 | 7271 | } |
michael@0 | 7272 | |
michael@0 | 7273 | |
michael@0 | 7274 | class nsUserDataCaller : public nsRunnable |
michael@0 | 7275 | { |
michael@0 | 7276 | public: |
michael@0 | 7277 | nsUserDataCaller(nsCOMArray<nsINode>& aNodesWithProperties, |
michael@0 | 7278 | nsIDocument* aOwnerDoc) |
michael@0 | 7279 | : mNodesWithProperties(aNodesWithProperties), |
michael@0 | 7280 | mOwnerDoc(aOwnerDoc) |
michael@0 | 7281 | { |
michael@0 | 7282 | } |
michael@0 | 7283 | |
michael@0 | 7284 | NS_IMETHOD Run() |
michael@0 | 7285 | { |
michael@0 | 7286 | nsNodeUtils::CallUserDataHandlers(mNodesWithProperties, mOwnerDoc, |
michael@0 | 7287 | nsIDOMUserDataHandler::NODE_ADOPTED, |
michael@0 | 7288 | false); |
michael@0 | 7289 | return NS_OK; |
michael@0 | 7290 | } |
michael@0 | 7291 | |
michael@0 | 7292 | private: |
michael@0 | 7293 | nsCOMArray<nsINode> mNodesWithProperties; |
michael@0 | 7294 | nsCOMPtr<nsIDocument> mOwnerDoc; |
michael@0 | 7295 | }; |
michael@0 | 7296 | |
michael@0 | 7297 | NS_IMETHODIMP |
michael@0 | 7298 | nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult) |
michael@0 | 7299 | { |
michael@0 | 7300 | *aResult = nullptr; |
michael@0 | 7301 | |
michael@0 | 7302 | nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode); |
michael@0 | 7303 | NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED); |
michael@0 | 7304 | |
michael@0 | 7305 | ErrorResult rv; |
michael@0 | 7306 | nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv); |
michael@0 | 7307 | if (rv.Failed()) { |
michael@0 | 7308 | return rv.ErrorCode(); |
michael@0 | 7309 | } |
michael@0 | 7310 | |
michael@0 | 7311 | NS_ADDREF(*aResult = result->AsDOMNode()); |
michael@0 | 7312 | return NS_OK; |
michael@0 | 7313 | } |
michael@0 | 7314 | |
michael@0 | 7315 | nsINode* |
michael@0 | 7316 | nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) |
michael@0 | 7317 | { |
michael@0 | 7318 | nsINode* adoptedNode = &aAdoptedNode; |
michael@0 | 7319 | |
michael@0 | 7320 | // Scope firing mutation events so that we don't carry any state that |
michael@0 | 7321 | // might be stale |
michael@0 | 7322 | { |
michael@0 | 7323 | nsINode* parent = adoptedNode->GetParentNode(); |
michael@0 | 7324 | if (parent) { |
michael@0 | 7325 | nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent, |
michael@0 | 7326 | adoptedNode->OwnerDoc()); |
michael@0 | 7327 | } |
michael@0 | 7328 | } |
michael@0 | 7329 | |
michael@0 | 7330 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 7331 | |
michael@0 | 7332 | switch (adoptedNode->NodeType()) { |
michael@0 | 7333 | case nsIDOMNode::ATTRIBUTE_NODE: |
michael@0 | 7334 | { |
michael@0 | 7335 | // Remove from ownerElement. |
michael@0 | 7336 | nsRefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode); |
michael@0 | 7337 | |
michael@0 | 7338 | nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv); |
michael@0 | 7339 | if (rv.Failed()) { |
michael@0 | 7340 | return nullptr; |
michael@0 | 7341 | } |
michael@0 | 7342 | |
michael@0 | 7343 | if (ownerElement) { |
michael@0 | 7344 | nsRefPtr<Attr> newAttr = |
michael@0 | 7345 | ownerElement->RemoveAttributeNode(*adoptedAttr, rv); |
michael@0 | 7346 | if (rv.Failed()) { |
michael@0 | 7347 | return nullptr; |
michael@0 | 7348 | } |
michael@0 | 7349 | |
michael@0 | 7350 | newAttr.swap(adoptedAttr); |
michael@0 | 7351 | } |
michael@0 | 7352 | |
michael@0 | 7353 | break; |
michael@0 | 7354 | } |
michael@0 | 7355 | case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: |
michael@0 | 7356 | case nsIDOMNode::ELEMENT_NODE: |
michael@0 | 7357 | case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: |
michael@0 | 7358 | case nsIDOMNode::TEXT_NODE: |
michael@0 | 7359 | case nsIDOMNode::CDATA_SECTION_NODE: |
michael@0 | 7360 | case nsIDOMNode::COMMENT_NODE: |
michael@0 | 7361 | case nsIDOMNode::DOCUMENT_TYPE_NODE: |
michael@0 | 7362 | { |
michael@0 | 7363 | // Don't allow adopting a node's anonymous subtree out from under it. |
michael@0 | 7364 | if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) { |
michael@0 | 7365 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 7366 | return nullptr; |
michael@0 | 7367 | } |
michael@0 | 7368 | |
michael@0 | 7369 | // We don't want to adopt an element into its own contentDocument or into |
michael@0 | 7370 | // a descendant contentDocument, so we check if the frameElement of this |
michael@0 | 7371 | // document or any of its parents is the adopted node or one of its |
michael@0 | 7372 | // descendants. |
michael@0 | 7373 | nsIDocument *doc = this; |
michael@0 | 7374 | do { |
michael@0 | 7375 | nsPIDOMWindow *win = doc->GetWindow(); |
michael@0 | 7376 | if (win) { |
michael@0 | 7377 | nsCOMPtr<nsINode> node = |
michael@0 | 7378 | do_QueryInterface(win->GetFrameElementInternal()); |
michael@0 | 7379 | if (node && |
michael@0 | 7380 | nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) { |
michael@0 | 7381 | rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
michael@0 | 7382 | return nullptr; |
michael@0 | 7383 | } |
michael@0 | 7384 | } |
michael@0 | 7385 | } while ((doc = doc->GetParentDocument())); |
michael@0 | 7386 | |
michael@0 | 7387 | // Remove from parent. |
michael@0 | 7388 | nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode(); |
michael@0 | 7389 | if (parent) { |
michael@0 | 7390 | int32_t idx = parent->IndexOf(adoptedNode); |
michael@0 | 7391 | MOZ_ASSERT(idx >= 0); |
michael@0 | 7392 | parent->RemoveChildAt(idx, true); |
michael@0 | 7393 | } else { |
michael@0 | 7394 | MOZ_ASSERT(!adoptedNode->IsInDoc()); |
michael@0 | 7395 | |
michael@0 | 7396 | // If we're adopting a node that's not in a document, it might still |
michael@0 | 7397 | // have a binding applied. Remove the binding from the element now |
michael@0 | 7398 | // that it's getting adopted into a new document. |
michael@0 | 7399 | // TODO Fully tear down the binding. |
michael@0 | 7400 | adoptedNode->AsContent()->SetXBLBinding(nullptr); |
michael@0 | 7401 | } |
michael@0 | 7402 | |
michael@0 | 7403 | break; |
michael@0 | 7404 | } |
michael@0 | 7405 | case nsIDOMNode::DOCUMENT_NODE: |
michael@0 | 7406 | { |
michael@0 | 7407 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 7408 | return nullptr; |
michael@0 | 7409 | } |
michael@0 | 7410 | default: |
michael@0 | 7411 | { |
michael@0 | 7412 | NS_WARNING("Don't know how to adopt this nodetype for adoptNode."); |
michael@0 | 7413 | |
michael@0 | 7414 | rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 7415 | return nullptr; |
michael@0 | 7416 | } |
michael@0 | 7417 | } |
michael@0 | 7418 | |
michael@0 | 7419 | nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc(); |
michael@0 | 7420 | bool sameDocument = oldDocument == this; |
michael@0 | 7421 | |
michael@0 | 7422 | AutoJSContext cx; |
michael@0 | 7423 | JS::Rooted<JSObject*> newScope(cx, nullptr); |
michael@0 | 7424 | if (!sameDocument) { |
michael@0 | 7425 | newScope = GetWrapper(); |
michael@0 | 7426 | if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) { |
michael@0 | 7427 | // Make sure cx is in a semi-sane compartment before we call WrapNative. |
michael@0 | 7428 | // It's kind of irrelevant, given that we're passing aAllowWrapping = |
michael@0 | 7429 | // false, and documents should always insist on being wrapped in an |
michael@0 | 7430 | // canonical scope. But we try to pass something sane anyway. |
michael@0 | 7431 | JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject()); |
michael@0 | 7432 | JS::Rooted<JS::Value> v(cx); |
michael@0 | 7433 | rv = nsContentUtils::WrapNative(cx, this, this, &v, |
michael@0 | 7434 | /* aAllowWrapping = */ false); |
michael@0 | 7435 | if (rv.Failed()) |
michael@0 | 7436 | return nullptr; |
michael@0 | 7437 | newScope = &v.toObject(); |
michael@0 | 7438 | } |
michael@0 | 7439 | } |
michael@0 | 7440 | |
michael@0 | 7441 | nsCOMArray<nsINode> nodesWithProperties; |
michael@0 | 7442 | rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager, |
michael@0 | 7443 | newScope, nodesWithProperties); |
michael@0 | 7444 | if (rv.Failed()) { |
michael@0 | 7445 | // Disconnect all nodes from their parents, since some have the old document |
michael@0 | 7446 | // as their ownerDocument and some have this as their ownerDocument. |
michael@0 | 7447 | BlastSubtreeToPieces(adoptedNode); |
michael@0 | 7448 | |
michael@0 | 7449 | if (!sameDocument && oldDocument) { |
michael@0 | 7450 | uint32_t count = nodesWithProperties.Count(); |
michael@0 | 7451 | for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) { |
michael@0 | 7452 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 7453 | // Remove all properties. |
michael@0 | 7454 | oldDocument->PropertyTable(j)-> |
michael@0 | 7455 | DeleteAllPropertiesFor(nodesWithProperties[i]); |
michael@0 | 7456 | } |
michael@0 | 7457 | } |
michael@0 | 7458 | } |
michael@0 | 7459 | |
michael@0 | 7460 | return nullptr; |
michael@0 | 7461 | } |
michael@0 | 7462 | |
michael@0 | 7463 | uint32_t count = nodesWithProperties.Count(); |
michael@0 | 7464 | if (!sameDocument && oldDocument) { |
michael@0 | 7465 | for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) { |
michael@0 | 7466 | nsPropertyTable *oldTable = oldDocument->PropertyTable(j); |
michael@0 | 7467 | nsPropertyTable *newTable = PropertyTable(j); |
michael@0 | 7468 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 7469 | rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i], |
michael@0 | 7470 | newTable); |
michael@0 | 7471 | } |
michael@0 | 7472 | } |
michael@0 | 7473 | |
michael@0 | 7474 | if (rv.Failed()) { |
michael@0 | 7475 | // Disconnect all nodes from their parents. |
michael@0 | 7476 | BlastSubtreeToPieces(adoptedNode); |
michael@0 | 7477 | |
michael@0 | 7478 | return nullptr; |
michael@0 | 7479 | } |
michael@0 | 7480 | } |
michael@0 | 7481 | |
michael@0 | 7482 | if (nodesWithProperties.Count()) { |
michael@0 | 7483 | nsContentUtils::AddScriptRunner(new nsUserDataCaller(nodesWithProperties, |
michael@0 | 7484 | this)); |
michael@0 | 7485 | } |
michael@0 | 7486 | |
michael@0 | 7487 | NS_ASSERTION(adoptedNode->OwnerDoc() == this, |
michael@0 | 7488 | "Should still be in the document we just got adopted into"); |
michael@0 | 7489 | |
michael@0 | 7490 | return adoptedNode; |
michael@0 | 7491 | } |
michael@0 | 7492 | |
michael@0 | 7493 | nsViewportInfo |
michael@0 | 7494 | nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize) |
michael@0 | 7495 | { |
michael@0 | 7496 | // In cases where the width of the CSS viewport is less than or equal to the width |
michael@0 | 7497 | // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom |
michael@0 | 7498 | // behaviour. See bug 941995 for details. |
michael@0 | 7499 | |
michael@0 | 7500 | switch (mViewportType) { |
michael@0 | 7501 | case DisplayWidthHeight: |
michael@0 | 7502 | return nsViewportInfo(aDisplaySize); |
michael@0 | 7503 | case DisplayWidthHeightNoZoom: |
michael@0 | 7504 | return nsViewportInfo(aDisplaySize, /*allowZoom*/ false, /*allowDoubleTapZoom*/ false); |
michael@0 | 7505 | case Unknown: |
michael@0 | 7506 | { |
michael@0 | 7507 | nsAutoString viewport; |
michael@0 | 7508 | GetHeaderData(nsGkAtoms::viewport, viewport); |
michael@0 | 7509 | if (viewport.IsEmpty()) { |
michael@0 | 7510 | // If the docType specifies that we are on a site optimized for mobile, |
michael@0 | 7511 | // then we want to return specially crafted defaults for the viewport info. |
michael@0 | 7512 | nsCOMPtr<nsIDOMDocumentType> docType; |
michael@0 | 7513 | nsresult rv = GetDoctype(getter_AddRefs(docType)); |
michael@0 | 7514 | if (NS_SUCCEEDED(rv) && docType) { |
michael@0 | 7515 | nsAutoString docId; |
michael@0 | 7516 | rv = docType->GetPublicId(docId); |
michael@0 | 7517 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 7518 | if ((docId.Find("WAP") != -1) || |
michael@0 | 7519 | (docId.Find("Mobile") != -1) || |
michael@0 | 7520 | (docId.Find("WML") != -1)) |
michael@0 | 7521 | { |
michael@0 | 7522 | // We're making an assumption that the docType can't change here |
michael@0 | 7523 | mViewportType = DisplayWidthHeight; |
michael@0 | 7524 | return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false); |
michael@0 | 7525 | } |
michael@0 | 7526 | } |
michael@0 | 7527 | } |
michael@0 | 7528 | |
michael@0 | 7529 | nsAutoString handheldFriendly; |
michael@0 | 7530 | GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly); |
michael@0 | 7531 | if (handheldFriendly.EqualsLiteral("true")) { |
michael@0 | 7532 | mViewportType = DisplayWidthHeight; |
michael@0 | 7533 | return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false); |
michael@0 | 7534 | } |
michael@0 | 7535 | |
michael@0 | 7536 | // Bug 940036. This is bad. When FirefoxOS was built, apps installed |
michael@0 | 7537 | // where not using the AsyncPanZoom code. As a result a lot of apps |
michael@0 | 7538 | // in the marketplace does not use it yet and instead are built to |
michael@0 | 7539 | // render correctly in FirefoxOS only. For a smooth transition the above |
michael@0 | 7540 | // code force installed apps to render as if they have a viewport with |
michael@0 | 7541 | // content="width=device-width, height=device-height, user-scalable=no". |
michael@0 | 7542 | // This could be safely remove once it is known that most apps in the |
michael@0 | 7543 | // marketplace use it and that users does not use an old version of the |
michael@0 | 7544 | // app that does not use it. |
michael@0 | 7545 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 7546 | if (docShell && docShell->GetIsApp()) { |
michael@0 | 7547 | nsString uri; |
michael@0 | 7548 | GetDocumentURI(uri); |
michael@0 | 7549 | if (!uri.EqualsLiteral("about:blank")) { |
michael@0 | 7550 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 7551 | NS_LITERAL_CSTRING("DOM"), this, |
michael@0 | 7552 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 7553 | "ImplicitMetaViewportTagFallback"); |
michael@0 | 7554 | } |
michael@0 | 7555 | mViewportType = DisplayWidthHeightNoZoom; |
michael@0 | 7556 | return nsViewportInfo(aDisplaySize, /*allowZoom*/false, /*allowDoubleTapZoom*/false); |
michael@0 | 7557 | } |
michael@0 | 7558 | } |
michael@0 | 7559 | |
michael@0 | 7560 | nsAutoString minScaleStr; |
michael@0 | 7561 | GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr); |
michael@0 | 7562 | |
michael@0 | 7563 | nsresult errorCode; |
michael@0 | 7564 | mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode)); |
michael@0 | 7565 | |
michael@0 | 7566 | if (NS_FAILED(errorCode)) { |
michael@0 | 7567 | mScaleMinFloat = kViewportMinScale; |
michael@0 | 7568 | } |
michael@0 | 7569 | |
michael@0 | 7570 | mScaleMinFloat = mozilla::clamped( |
michael@0 | 7571 | mScaleMinFloat, kViewportMinScale, kViewportMaxScale); |
michael@0 | 7572 | |
michael@0 | 7573 | nsAutoString maxScaleStr; |
michael@0 | 7574 | GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr); |
michael@0 | 7575 | |
michael@0 | 7576 | // We define a special error code variable for the scale and max scale, |
michael@0 | 7577 | // because they are used later (see the width calculations). |
michael@0 | 7578 | nsresult scaleMaxErrorCode; |
michael@0 | 7579 | mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode)); |
michael@0 | 7580 | |
michael@0 | 7581 | if (NS_FAILED(scaleMaxErrorCode)) { |
michael@0 | 7582 | mScaleMaxFloat = kViewportMaxScale; |
michael@0 | 7583 | } |
michael@0 | 7584 | |
michael@0 | 7585 | mScaleMaxFloat = mozilla::clamped( |
michael@0 | 7586 | mScaleMaxFloat, kViewportMinScale, kViewportMaxScale); |
michael@0 | 7587 | |
michael@0 | 7588 | nsAutoString scaleStr; |
michael@0 | 7589 | GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr); |
michael@0 | 7590 | |
michael@0 | 7591 | nsresult scaleErrorCode; |
michael@0 | 7592 | mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode)); |
michael@0 | 7593 | |
michael@0 | 7594 | nsAutoString widthStr, heightStr; |
michael@0 | 7595 | |
michael@0 | 7596 | GetHeaderData(nsGkAtoms::viewport_height, heightStr); |
michael@0 | 7597 | GetHeaderData(nsGkAtoms::viewport_width, widthStr); |
michael@0 | 7598 | |
michael@0 | 7599 | mAutoSize = false; |
michael@0 | 7600 | |
michael@0 | 7601 | if (widthStr.EqualsLiteral("device-width")) { |
michael@0 | 7602 | mAutoSize = true; |
michael@0 | 7603 | } |
michael@0 | 7604 | |
michael@0 | 7605 | if (widthStr.IsEmpty() && |
michael@0 | 7606 | (heightStr.EqualsLiteral("device-height") || |
michael@0 | 7607 | (mScaleFloat.scale == 1.0))) |
michael@0 | 7608 | { |
michael@0 | 7609 | mAutoSize = true; |
michael@0 | 7610 | } |
michael@0 | 7611 | |
michael@0 | 7612 | nsresult widthErrorCode, heightErrorCode; |
michael@0 | 7613 | mViewportSize.width = widthStr.ToInteger(&widthErrorCode); |
michael@0 | 7614 | mViewportSize.height = heightStr.ToInteger(&heightErrorCode); |
michael@0 | 7615 | |
michael@0 | 7616 | // If width or height has not been set to a valid number by this point, |
michael@0 | 7617 | // fall back to a default value. |
michael@0 | 7618 | mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0); |
michael@0 | 7619 | mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0); |
michael@0 | 7620 | |
michael@0 | 7621 | mAllowZoom = true; |
michael@0 | 7622 | nsAutoString userScalable; |
michael@0 | 7623 | GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable); |
michael@0 | 7624 | |
michael@0 | 7625 | if ((userScalable.EqualsLiteral("0")) || |
michael@0 | 7626 | (userScalable.EqualsLiteral("no")) || |
michael@0 | 7627 | (userScalable.EqualsLiteral("false"))) { |
michael@0 | 7628 | mAllowZoom = false; |
michael@0 | 7629 | } |
michael@0 | 7630 | mAllowDoubleTapZoom = mAllowZoom; |
michael@0 | 7631 | |
michael@0 | 7632 | mScaleStrEmpty = scaleStr.IsEmpty(); |
michael@0 | 7633 | mWidthStrEmpty = widthStr.IsEmpty(); |
michael@0 | 7634 | mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode); |
michael@0 | 7635 | mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode); |
michael@0 | 7636 | |
michael@0 | 7637 | mViewportType = Specified; |
michael@0 | 7638 | } |
michael@0 | 7639 | case Specified: |
michael@0 | 7640 | default: |
michael@0 | 7641 | CSSIntSize size = mViewportSize; |
michael@0 | 7642 | |
michael@0 | 7643 | if (!mValidWidth) { |
michael@0 | 7644 | if (mValidHeight && !aDisplaySize.IsEmpty()) { |
michael@0 | 7645 | size.width = int32_t(size.height * aDisplaySize.width / aDisplaySize.height); |
michael@0 | 7646 | } else { |
michael@0 | 7647 | size.width = Preferences::GetInt("browser.viewport.desktopWidth", |
michael@0 | 7648 | kViewportDefaultScreenWidth); |
michael@0 | 7649 | } |
michael@0 | 7650 | } |
michael@0 | 7651 | |
michael@0 | 7652 | if (!mValidHeight) { |
michael@0 | 7653 | if (!aDisplaySize.IsEmpty()) { |
michael@0 | 7654 | size.height = int32_t(size.width * aDisplaySize.height / aDisplaySize.width); |
michael@0 | 7655 | } else { |
michael@0 | 7656 | size.height = size.width; |
michael@0 | 7657 | } |
michael@0 | 7658 | } |
michael@0 | 7659 | // Now convert the scale into device pixels per CSS pixel. |
michael@0 | 7660 | nsIWidget *widget = nsContentUtils::WidgetForDocument(this); |
michael@0 | 7661 | CSSToLayoutDeviceScale pixelRatio = widget ? widget->GetDefaultScale() |
michael@0 | 7662 | : CSSToLayoutDeviceScale(1.0f); |
michael@0 | 7663 | CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio; |
michael@0 | 7664 | CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio; |
michael@0 | 7665 | CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio; |
michael@0 | 7666 | |
michael@0 | 7667 | if (mAutoSize) { |
michael@0 | 7668 | // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size. |
michael@0 | 7669 | CSSToScreenScale defaultPixelScale = pixelRatio * LayoutDeviceToScreenScale(1.0f); |
michael@0 | 7670 | size = mozilla::gfx::RoundedToInt(ScreenSize(aDisplaySize) / defaultPixelScale); |
michael@0 | 7671 | } |
michael@0 | 7672 | |
michael@0 | 7673 | size.width = clamped(size.width, kViewportMinSize.width, kViewportMaxSize.width); |
michael@0 | 7674 | |
michael@0 | 7675 | // Also recalculate the default zoom, if it wasn't specified in the metadata, |
michael@0 | 7676 | // and the width is specified. |
michael@0 | 7677 | if (mScaleStrEmpty && !mWidthStrEmpty) { |
michael@0 | 7678 | CSSToScreenScale defaultScale(float(aDisplaySize.width) / float(size.width)); |
michael@0 | 7679 | scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale; |
michael@0 | 7680 | } |
michael@0 | 7681 | |
michael@0 | 7682 | size.height = clamped(size.height, kViewportMinSize.height, kViewportMaxSize.height); |
michael@0 | 7683 | |
michael@0 | 7684 | // We need to perform a conversion, but only if the initial or maximum |
michael@0 | 7685 | // scale were set explicitly by the user. |
michael@0 | 7686 | if (mValidScaleFloat) { |
michael@0 | 7687 | CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleFloat); |
michael@0 | 7688 | size.width = std::max(size.width, displaySize.width); |
michael@0 | 7689 | size.height = std::max(size.height, displaySize.height); |
michael@0 | 7690 | } else if (mValidMaxScale) { |
michael@0 | 7691 | CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleMaxFloat); |
michael@0 | 7692 | size.width = std::max(size.width, displaySize.width); |
michael@0 | 7693 | size.height = std::max(size.height, displaySize.height); |
michael@0 | 7694 | } |
michael@0 | 7695 | |
michael@0 | 7696 | return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size, |
michael@0 | 7697 | mAutoSize, mAllowZoom, mAllowDoubleTapZoom); |
michael@0 | 7698 | } |
michael@0 | 7699 | } |
michael@0 | 7700 | |
michael@0 | 7701 | EventListenerManager* |
michael@0 | 7702 | nsDocument::GetOrCreateListenerManager() |
michael@0 | 7703 | { |
michael@0 | 7704 | if (!mListenerManager) { |
michael@0 | 7705 | mListenerManager = |
michael@0 | 7706 | new EventListenerManager(static_cast<EventTarget*>(this)); |
michael@0 | 7707 | SetFlags(NODE_HAS_LISTENERMANAGER); |
michael@0 | 7708 | } |
michael@0 | 7709 | |
michael@0 | 7710 | return mListenerManager; |
michael@0 | 7711 | } |
michael@0 | 7712 | |
michael@0 | 7713 | EventListenerManager* |
michael@0 | 7714 | nsDocument::GetExistingListenerManager() const |
michael@0 | 7715 | { |
michael@0 | 7716 | return mListenerManager; |
michael@0 | 7717 | } |
michael@0 | 7718 | |
michael@0 | 7719 | nsresult |
michael@0 | 7720 | nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor) |
michael@0 | 7721 | { |
michael@0 | 7722 | aVisitor.mCanHandle = true; |
michael@0 | 7723 | // FIXME! This is a hack to make middle mouse paste working also in Editor. |
michael@0 | 7724 | // Bug 329119 |
michael@0 | 7725 | aVisitor.mForceContentDispatch = true; |
michael@0 | 7726 | |
michael@0 | 7727 | // Load events must not propagate to |window| object, see bug 335251. |
michael@0 | 7728 | if (aVisitor.mEvent->message != NS_LOAD) { |
michael@0 | 7729 | nsGlobalWindow* window = static_cast<nsGlobalWindow*>(GetWindow()); |
michael@0 | 7730 | aVisitor.mParentTarget = |
michael@0 | 7731 | window ? window->GetTargetForEventTargetChain() : nullptr; |
michael@0 | 7732 | } |
michael@0 | 7733 | return NS_OK; |
michael@0 | 7734 | } |
michael@0 | 7735 | |
michael@0 | 7736 | NS_IMETHODIMP |
michael@0 | 7737 | nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn) |
michael@0 | 7738 | { |
michael@0 | 7739 | NS_ENSURE_ARG_POINTER(aReturn); |
michael@0 | 7740 | ErrorResult rv; |
michael@0 | 7741 | *aReturn = nsIDocument::CreateEvent(aEventType, rv).take(); |
michael@0 | 7742 | return rv.ErrorCode(); |
michael@0 | 7743 | } |
michael@0 | 7744 | |
michael@0 | 7745 | already_AddRefed<Event> |
michael@0 | 7746 | nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const |
michael@0 | 7747 | { |
michael@0 | 7748 | nsIPresShell *shell = GetShell(); |
michael@0 | 7749 | |
michael@0 | 7750 | nsPresContext *presContext = nullptr; |
michael@0 | 7751 | |
michael@0 | 7752 | if (shell) { |
michael@0 | 7753 | // Retrieve the context |
michael@0 | 7754 | presContext = shell->GetPresContext(); |
michael@0 | 7755 | } |
michael@0 | 7756 | |
michael@0 | 7757 | // Create event even without presContext. |
michael@0 | 7758 | nsCOMPtr<nsIDOMEvent> ev; |
michael@0 | 7759 | rv = EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), |
michael@0 | 7760 | presContext, nullptr, aEventType, |
michael@0 | 7761 | getter_AddRefs(ev)); |
michael@0 | 7762 | if (!ev) { |
michael@0 | 7763 | return nullptr; |
michael@0 | 7764 | } |
michael@0 | 7765 | WidgetEvent* e = ev->GetInternalNSEvent(); |
michael@0 | 7766 | e->mFlags.mBubbles = false; |
michael@0 | 7767 | e->mFlags.mCancelable = false; |
michael@0 | 7768 | return dont_AddRef(ev.forget().take()->InternalDOMEvent()); |
michael@0 | 7769 | } |
michael@0 | 7770 | |
michael@0 | 7771 | void |
michael@0 | 7772 | nsDocument::FlushPendingNotifications(mozFlushType aType) |
michael@0 | 7773 | { |
michael@0 | 7774 | nsDocumentOnStack dos(this); |
michael@0 | 7775 | |
michael@0 | 7776 | // We need to flush the sink for non-HTML documents (because the XML |
michael@0 | 7777 | // parser still does insertion with deferred notifications). We |
michael@0 | 7778 | // also need to flush the sink if this is a layout-related flush, to |
michael@0 | 7779 | // make sure that layout is started as needed. But we can skip that |
michael@0 | 7780 | // part if we have no presshell or if it's already done an initial |
michael@0 | 7781 | // reflow. |
michael@0 | 7782 | if ((!IsHTML() || |
michael@0 | 7783 | (aType > Flush_ContentAndNotify && mPresShell && |
michael@0 | 7784 | !mPresShell->DidInitialize())) && |
michael@0 | 7785 | (mParser || mWeakSink)) { |
michael@0 | 7786 | nsCOMPtr<nsIContentSink> sink; |
michael@0 | 7787 | if (mParser) { |
michael@0 | 7788 | sink = mParser->GetContentSink(); |
michael@0 | 7789 | } else { |
michael@0 | 7790 | sink = do_QueryReferent(mWeakSink); |
michael@0 | 7791 | if (!sink) { |
michael@0 | 7792 | mWeakSink = nullptr; |
michael@0 | 7793 | } |
michael@0 | 7794 | } |
michael@0 | 7795 | // Determine if it is safe to flush the sink notifications |
michael@0 | 7796 | // by determining if it safe to flush all the presshells. |
michael@0 | 7797 | if (sink && (aType == Flush_Content || IsSafeToFlush())) { |
michael@0 | 7798 | sink->FlushPendingNotifications(aType); |
michael@0 | 7799 | } |
michael@0 | 7800 | } |
michael@0 | 7801 | |
michael@0 | 7802 | // Should we be flushing pending binding constructors in here? |
michael@0 | 7803 | |
michael@0 | 7804 | if (aType <= Flush_ContentAndNotify) { |
michael@0 | 7805 | // Nothing to do here |
michael@0 | 7806 | return; |
michael@0 | 7807 | } |
michael@0 | 7808 | |
michael@0 | 7809 | // If we have a parent we must flush the parent too to ensure that our |
michael@0 | 7810 | // container is reflowed if its size was changed. But if it's not safe to |
michael@0 | 7811 | // flush ourselves, then don't flush the parent, since that can cause things |
michael@0 | 7812 | // like resizes of our frame's widget, which we can't handle while flushing |
michael@0 | 7813 | // is unsafe. |
michael@0 | 7814 | // Since media queries mean that a size change of our container can |
michael@0 | 7815 | // affect style, we need to promote a style flush on ourself to a |
michael@0 | 7816 | // layout flush on our parent, since we need our container to be the |
michael@0 | 7817 | // correct size to determine the correct style. |
michael@0 | 7818 | if (mParentDocument && IsSafeToFlush()) { |
michael@0 | 7819 | mozFlushType parentType = aType; |
michael@0 | 7820 | if (aType >= Flush_Style) |
michael@0 | 7821 | parentType = std::max(Flush_Layout, aType); |
michael@0 | 7822 | mParentDocument->FlushPendingNotifications(parentType); |
michael@0 | 7823 | } |
michael@0 | 7824 | |
michael@0 | 7825 | // We can optimize away getting our presshell and calling |
michael@0 | 7826 | // FlushPendingNotifications on it if we don't need a flush of the sort we're |
michael@0 | 7827 | // looking at. The one exception is if mInFlush is true, because in that |
michael@0 | 7828 | // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false |
michael@0 | 7829 | // already but the presshell hasn't actually done the corresponding work yet. |
michael@0 | 7830 | // So if mInFlush and reentering this code, we need to flush the presshell. |
michael@0 | 7831 | if (mNeedStyleFlush || |
michael@0 | 7832 | (mNeedLayoutFlush && aType >= Flush_InterruptibleLayout) || |
michael@0 | 7833 | aType >= Flush_Display || |
michael@0 | 7834 | mInFlush) { |
michael@0 | 7835 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 7836 | if (shell) { |
michael@0 | 7837 | mNeedStyleFlush = false; |
michael@0 | 7838 | mNeedLayoutFlush = mNeedLayoutFlush && (aType < Flush_InterruptibleLayout); |
michael@0 | 7839 | // mInFlush is a bitfield, so can't us AutoRestore here. But we |
michael@0 | 7840 | // need to keep track of multi-level reentry correctly, so need |
michael@0 | 7841 | // to restore the old mInFlush value. |
michael@0 | 7842 | bool oldInFlush = mInFlush; |
michael@0 | 7843 | mInFlush = true; |
michael@0 | 7844 | shell->FlushPendingNotifications(aType); |
michael@0 | 7845 | mInFlush = oldInFlush; |
michael@0 | 7846 | } |
michael@0 | 7847 | } |
michael@0 | 7848 | } |
michael@0 | 7849 | |
michael@0 | 7850 | static bool |
michael@0 | 7851 | Copy(nsIDocument* aDocument, void* aData) |
michael@0 | 7852 | { |
michael@0 | 7853 | nsTArray<nsCOMPtr<nsIDocument> >* resources = |
michael@0 | 7854 | static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData); |
michael@0 | 7855 | resources->AppendElement(aDocument); |
michael@0 | 7856 | return true; |
michael@0 | 7857 | } |
michael@0 | 7858 | |
michael@0 | 7859 | void |
michael@0 | 7860 | nsDocument::FlushExternalResources(mozFlushType aType) |
michael@0 | 7861 | { |
michael@0 | 7862 | NS_ASSERTION(aType >= Flush_Style, |
michael@0 | 7863 | "should only need to flush for style or higher in external resources"); |
michael@0 | 7864 | if (GetDisplayDocument()) { |
michael@0 | 7865 | return; |
michael@0 | 7866 | } |
michael@0 | 7867 | nsTArray<nsCOMPtr<nsIDocument> > resources; |
michael@0 | 7868 | EnumerateExternalResources(Copy, &resources); |
michael@0 | 7869 | |
michael@0 | 7870 | for (uint32_t i = 0; i < resources.Length(); i++) { |
michael@0 | 7871 | resources[i]->FlushPendingNotifications(aType); |
michael@0 | 7872 | } |
michael@0 | 7873 | } |
michael@0 | 7874 | |
michael@0 | 7875 | void |
michael@0 | 7876 | nsDocument::SetXMLDeclaration(const char16_t *aVersion, |
michael@0 | 7877 | const char16_t *aEncoding, |
michael@0 | 7878 | const int32_t aStandalone) |
michael@0 | 7879 | { |
michael@0 | 7880 | if (!aVersion || *aVersion == '\0') { |
michael@0 | 7881 | mXMLDeclarationBits = 0; |
michael@0 | 7882 | return; |
michael@0 | 7883 | } |
michael@0 | 7884 | |
michael@0 | 7885 | mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS; |
michael@0 | 7886 | |
michael@0 | 7887 | if (aEncoding && *aEncoding != '\0') { |
michael@0 | 7888 | mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS; |
michael@0 | 7889 | } |
michael@0 | 7890 | |
michael@0 | 7891 | if (aStandalone == 1) { |
michael@0 | 7892 | mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS | |
michael@0 | 7893 | XML_DECLARATION_BITS_STANDALONE_YES; |
michael@0 | 7894 | } |
michael@0 | 7895 | else if (aStandalone == 0) { |
michael@0 | 7896 | mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS; |
michael@0 | 7897 | } |
michael@0 | 7898 | } |
michael@0 | 7899 | |
michael@0 | 7900 | void |
michael@0 | 7901 | nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding, |
michael@0 | 7902 | nsAString& aStandalone) |
michael@0 | 7903 | { |
michael@0 | 7904 | aVersion.Truncate(); |
michael@0 | 7905 | aEncoding.Truncate(); |
michael@0 | 7906 | aStandalone.Truncate(); |
michael@0 | 7907 | |
michael@0 | 7908 | if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) { |
michael@0 | 7909 | return; |
michael@0 | 7910 | } |
michael@0 | 7911 | |
michael@0 | 7912 | // always until we start supporting 1.1 etc. |
michael@0 | 7913 | aVersion.AssignLiteral("1.0"); |
michael@0 | 7914 | |
michael@0 | 7915 | if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) { |
michael@0 | 7916 | // This is what we have stored, not necessarily what was written |
michael@0 | 7917 | // in the original |
michael@0 | 7918 | GetCharacterSet(aEncoding); |
michael@0 | 7919 | } |
michael@0 | 7920 | |
michael@0 | 7921 | if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) { |
michael@0 | 7922 | if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) { |
michael@0 | 7923 | aStandalone.AssignLiteral("yes"); |
michael@0 | 7924 | } else { |
michael@0 | 7925 | aStandalone.AssignLiteral("no"); |
michael@0 | 7926 | } |
michael@0 | 7927 | } |
michael@0 | 7928 | } |
michael@0 | 7929 | |
michael@0 | 7930 | bool |
michael@0 | 7931 | nsDocument::IsScriptEnabled() |
michael@0 | 7932 | { |
michael@0 | 7933 | // If this document is sandboxed without 'allow-scripts' |
michael@0 | 7934 | // script is not enabled |
michael@0 | 7935 | if (mSandboxFlags & SANDBOXED_SCRIPTS) { |
michael@0 | 7936 | return false; |
michael@0 | 7937 | } |
michael@0 | 7938 | |
michael@0 | 7939 | nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); |
michael@0 | 7940 | NS_ENSURE_TRUE(sm, false); |
michael@0 | 7941 | |
michael@0 | 7942 | nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow()); |
michael@0 | 7943 | NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false); |
michael@0 | 7944 | |
michael@0 | 7945 | return sm->ScriptAllowed(globalObject->GetGlobalJSObject()); |
michael@0 | 7946 | } |
michael@0 | 7947 | |
michael@0 | 7948 | nsRadioGroupStruct* |
michael@0 | 7949 | nsDocument::GetRadioGroupInternal(const nsAString& aName) const |
michael@0 | 7950 | { |
michael@0 | 7951 | #ifdef DEBUG |
michael@0 | 7952 | if (IsHTML()) { |
michael@0 | 7953 | nsAutoString lcName; |
michael@0 | 7954 | ToLowerCase(aName, lcName); |
michael@0 | 7955 | MOZ_ASSERT(aName == lcName); |
michael@0 | 7956 | } |
michael@0 | 7957 | #endif |
michael@0 | 7958 | |
michael@0 | 7959 | nsRadioGroupStruct* radioGroup; |
michael@0 | 7960 | if (!mRadioGroups.Get(aName, &radioGroup)) { |
michael@0 | 7961 | return nullptr; |
michael@0 | 7962 | } |
michael@0 | 7963 | |
michael@0 | 7964 | return radioGroup; |
michael@0 | 7965 | } |
michael@0 | 7966 | |
michael@0 | 7967 | nsRadioGroupStruct* |
michael@0 | 7968 | nsDocument::GetRadioGroup(const nsAString& aName) const |
michael@0 | 7969 | { |
michael@0 | 7970 | nsAutoString tmKey(aName); |
michael@0 | 7971 | if (IsHTML()) { |
michael@0 | 7972 | ToLowerCase(tmKey); //should case-insensitive. |
michael@0 | 7973 | } |
michael@0 | 7974 | |
michael@0 | 7975 | return GetRadioGroupInternal(tmKey); |
michael@0 | 7976 | } |
michael@0 | 7977 | |
michael@0 | 7978 | nsRadioGroupStruct* |
michael@0 | 7979 | nsDocument::GetOrCreateRadioGroup(const nsAString& aName) |
michael@0 | 7980 | { |
michael@0 | 7981 | nsAutoString tmKey(aName); |
michael@0 | 7982 | if (IsHTML()) { |
michael@0 | 7983 | ToLowerCase(tmKey); //should case-insensitive. |
michael@0 | 7984 | } |
michael@0 | 7985 | |
michael@0 | 7986 | if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) { |
michael@0 | 7987 | return radioGroup; |
michael@0 | 7988 | } |
michael@0 | 7989 | |
michael@0 | 7990 | nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct()); |
michael@0 | 7991 | mRadioGroups.Put(tmKey, newRadioGroup); |
michael@0 | 7992 | |
michael@0 | 7993 | return newRadioGroup.forget(); |
michael@0 | 7994 | } |
michael@0 | 7995 | |
michael@0 | 7996 | void |
michael@0 | 7997 | nsDocument::SetCurrentRadioButton(const nsAString& aName, |
michael@0 | 7998 | HTMLInputElement* aRadio) |
michael@0 | 7999 | { |
michael@0 | 8000 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8001 | radioGroup->mSelectedRadioButton = aRadio; |
michael@0 | 8002 | } |
michael@0 | 8003 | |
michael@0 | 8004 | HTMLInputElement* |
michael@0 | 8005 | nsDocument::GetCurrentRadioButton(const nsAString& aName) |
michael@0 | 8006 | { |
michael@0 | 8007 | return GetOrCreateRadioGroup(aName)->mSelectedRadioButton; |
michael@0 | 8008 | } |
michael@0 | 8009 | |
michael@0 | 8010 | NS_IMETHODIMP |
michael@0 | 8011 | nsDocument::GetNextRadioButton(const nsAString& aName, |
michael@0 | 8012 | const bool aPrevious, |
michael@0 | 8013 | HTMLInputElement* aFocusedRadio, |
michael@0 | 8014 | HTMLInputElement** aRadioOut) |
michael@0 | 8015 | { |
michael@0 | 8016 | // XXX Can we combine the HTML radio button method impls of |
michael@0 | 8017 | // nsDocument and nsHTMLFormControl? |
michael@0 | 8018 | // XXX Why is HTML radio button stuff in nsDocument, as |
michael@0 | 8019 | // opposed to nsHTMLDocument? |
michael@0 | 8020 | *aRadioOut = nullptr; |
michael@0 | 8021 | |
michael@0 | 8022 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8023 | |
michael@0 | 8024 | // Return the radio button relative to the focused radio button. |
michael@0 | 8025 | // If no radio is focused, get the radio relative to the selected one. |
michael@0 | 8026 | nsRefPtr<HTMLInputElement> currentRadio; |
michael@0 | 8027 | if (aFocusedRadio) { |
michael@0 | 8028 | currentRadio = aFocusedRadio; |
michael@0 | 8029 | } |
michael@0 | 8030 | else { |
michael@0 | 8031 | currentRadio = radioGroup->mSelectedRadioButton; |
michael@0 | 8032 | if (!currentRadio) { |
michael@0 | 8033 | return NS_ERROR_FAILURE; |
michael@0 | 8034 | } |
michael@0 | 8035 | } |
michael@0 | 8036 | int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio); |
michael@0 | 8037 | if (index < 0) { |
michael@0 | 8038 | return NS_ERROR_FAILURE; |
michael@0 | 8039 | } |
michael@0 | 8040 | |
michael@0 | 8041 | int32_t numRadios = radioGroup->mRadioButtons.Count(); |
michael@0 | 8042 | nsRefPtr<HTMLInputElement> radio; |
michael@0 | 8043 | do { |
michael@0 | 8044 | if (aPrevious) { |
michael@0 | 8045 | if (--index < 0) { |
michael@0 | 8046 | index = numRadios -1; |
michael@0 | 8047 | } |
michael@0 | 8048 | } |
michael@0 | 8049 | else if (++index >= numRadios) { |
michael@0 | 8050 | index = 0; |
michael@0 | 8051 | } |
michael@0 | 8052 | NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTML(nsGkAtoms::input), |
michael@0 | 8053 | "mRadioButtons holding a non-radio button"); |
michael@0 | 8054 | radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]); |
michael@0 | 8055 | } while (radio->Disabled() && radio != currentRadio); |
michael@0 | 8056 | |
michael@0 | 8057 | radio.forget(aRadioOut); |
michael@0 | 8058 | return NS_OK; |
michael@0 | 8059 | } |
michael@0 | 8060 | |
michael@0 | 8061 | void |
michael@0 | 8062 | nsDocument::AddToRadioGroup(const nsAString& aName, |
michael@0 | 8063 | nsIFormControl* aRadio) |
michael@0 | 8064 | { |
michael@0 | 8065 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8066 | radioGroup->mRadioButtons.AppendObject(aRadio); |
michael@0 | 8067 | |
michael@0 | 8068 | nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio); |
michael@0 | 8069 | NS_ASSERTION(element, "radio controls have to be content elements"); |
michael@0 | 8070 | if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { |
michael@0 | 8071 | radioGroup->mRequiredRadioCount++; |
michael@0 | 8072 | } |
michael@0 | 8073 | } |
michael@0 | 8074 | |
michael@0 | 8075 | void |
michael@0 | 8076 | nsDocument::RemoveFromRadioGroup(const nsAString& aName, |
michael@0 | 8077 | nsIFormControl* aRadio) |
michael@0 | 8078 | { |
michael@0 | 8079 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8080 | radioGroup->mRadioButtons.RemoveObject(aRadio); |
michael@0 | 8081 | |
michael@0 | 8082 | nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio); |
michael@0 | 8083 | NS_ASSERTION(element, "radio controls have to be content elements"); |
michael@0 | 8084 | if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { |
michael@0 | 8085 | NS_ASSERTION(radioGroup->mRequiredRadioCount != 0, |
michael@0 | 8086 | "mRequiredRadioCount about to wrap below 0!"); |
michael@0 | 8087 | radioGroup->mRequiredRadioCount--; |
michael@0 | 8088 | } |
michael@0 | 8089 | } |
michael@0 | 8090 | |
michael@0 | 8091 | NS_IMETHODIMP |
michael@0 | 8092 | nsDocument::WalkRadioGroup(const nsAString& aName, |
michael@0 | 8093 | nsIRadioVisitor* aVisitor, |
michael@0 | 8094 | bool aFlushContent) |
michael@0 | 8095 | { |
michael@0 | 8096 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8097 | |
michael@0 | 8098 | for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) { |
michael@0 | 8099 | if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) { |
michael@0 | 8100 | return NS_OK; |
michael@0 | 8101 | } |
michael@0 | 8102 | } |
michael@0 | 8103 | |
michael@0 | 8104 | return NS_OK; |
michael@0 | 8105 | } |
michael@0 | 8106 | |
michael@0 | 8107 | uint32_t |
michael@0 | 8108 | nsDocument::GetRequiredRadioCount(const nsAString& aName) const |
michael@0 | 8109 | { |
michael@0 | 8110 | nsRadioGroupStruct* radioGroup = GetRadioGroup(aName); |
michael@0 | 8111 | return radioGroup ? radioGroup->mRequiredRadioCount : 0; |
michael@0 | 8112 | } |
michael@0 | 8113 | |
michael@0 | 8114 | void |
michael@0 | 8115 | nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio) |
michael@0 | 8116 | { |
michael@0 | 8117 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8118 | |
michael@0 | 8119 | nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio); |
michael@0 | 8120 | NS_ASSERTION(element, "radio controls have to be content elements"); |
michael@0 | 8121 | if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { |
michael@0 | 8122 | radioGroup->mRequiredRadioCount++; |
michael@0 | 8123 | } else { |
michael@0 | 8124 | NS_ASSERTION(radioGroup->mRequiredRadioCount != 0, |
michael@0 | 8125 | "mRequiredRadioCount about to wrap below 0!"); |
michael@0 | 8126 | radioGroup->mRequiredRadioCount--; |
michael@0 | 8127 | } |
michael@0 | 8128 | } |
michael@0 | 8129 | |
michael@0 | 8130 | bool |
michael@0 | 8131 | nsDocument::GetValueMissingState(const nsAString& aName) const |
michael@0 | 8132 | { |
michael@0 | 8133 | nsRadioGroupStruct* radioGroup = GetRadioGroup(aName); |
michael@0 | 8134 | return radioGroup && radioGroup->mGroupSuffersFromValueMissing; |
michael@0 | 8135 | } |
michael@0 | 8136 | |
michael@0 | 8137 | void |
michael@0 | 8138 | nsDocument::SetValueMissingState(const nsAString& aName, bool aValue) |
michael@0 | 8139 | { |
michael@0 | 8140 | nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName); |
michael@0 | 8141 | radioGroup->mGroupSuffersFromValueMissing = aValue; |
michael@0 | 8142 | } |
michael@0 | 8143 | |
michael@0 | 8144 | void |
michael@0 | 8145 | nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) |
michael@0 | 8146 | { |
michael@0 | 8147 | nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); |
michael@0 | 8148 | PRTime modDate = 0; |
michael@0 | 8149 | nsresult rv; |
michael@0 | 8150 | |
michael@0 | 8151 | if (httpChannel) { |
michael@0 | 8152 | nsAutoCString tmp; |
michael@0 | 8153 | rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), |
michael@0 | 8154 | tmp); |
michael@0 | 8155 | |
michael@0 | 8156 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 8157 | PRTime time; |
michael@0 | 8158 | PRStatus st = PR_ParseTimeString(tmp.get(), true, &time); |
michael@0 | 8159 | if (st == PR_SUCCESS) { |
michael@0 | 8160 | modDate = time; |
michael@0 | 8161 | } |
michael@0 | 8162 | } |
michael@0 | 8163 | |
michael@0 | 8164 | // The misspelled key 'referer' is as per the HTTP spec |
michael@0 | 8165 | rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"), |
michael@0 | 8166 | mReferrer); |
michael@0 | 8167 | if (NS_FAILED(rv)) { |
michael@0 | 8168 | mReferrer.Truncate(); |
michael@0 | 8169 | } |
michael@0 | 8170 | |
michael@0 | 8171 | static const char *const headers[] = { |
michael@0 | 8172 | "default-style", |
michael@0 | 8173 | "content-style-type", |
michael@0 | 8174 | "content-language", |
michael@0 | 8175 | "content-disposition", |
michael@0 | 8176 | "refresh", |
michael@0 | 8177 | "x-dns-prefetch-control", |
michael@0 | 8178 | "x-frame-options", |
michael@0 | 8179 | // add more http headers if you need |
michael@0 | 8180 | // XXXbz don't add content-location support without reading bug |
michael@0 | 8181 | // 238654 and its dependencies/dups first. |
michael@0 | 8182 | 0 |
michael@0 | 8183 | }; |
michael@0 | 8184 | |
michael@0 | 8185 | nsAutoCString headerVal; |
michael@0 | 8186 | const char *const *name = headers; |
michael@0 | 8187 | while (*name) { |
michael@0 | 8188 | rv = |
michael@0 | 8189 | httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal); |
michael@0 | 8190 | if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) { |
michael@0 | 8191 | nsCOMPtr<nsIAtom> key = do_GetAtom(*name); |
michael@0 | 8192 | SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal)); |
michael@0 | 8193 | } |
michael@0 | 8194 | ++name; |
michael@0 | 8195 | } |
michael@0 | 8196 | } else { |
michael@0 | 8197 | nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel); |
michael@0 | 8198 | if (fileChannel) { |
michael@0 | 8199 | nsCOMPtr<nsIFile> file; |
michael@0 | 8200 | fileChannel->GetFile(getter_AddRefs(file)); |
michael@0 | 8201 | if (file) { |
michael@0 | 8202 | PRTime msecs; |
michael@0 | 8203 | rv = file->GetLastModifiedTime(&msecs); |
michael@0 | 8204 | |
michael@0 | 8205 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 8206 | modDate = msecs * int64_t(PR_USEC_PER_MSEC); |
michael@0 | 8207 | } |
michael@0 | 8208 | } |
michael@0 | 8209 | } else { |
michael@0 | 8210 | nsAutoCString contentDisp; |
michael@0 | 8211 | rv = aChannel->GetContentDispositionHeader(contentDisp); |
michael@0 | 8212 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 8213 | SetHeaderData(nsGkAtoms::headerContentDisposition, |
michael@0 | 8214 | NS_ConvertASCIItoUTF16(contentDisp)); |
michael@0 | 8215 | } |
michael@0 | 8216 | } |
michael@0 | 8217 | } |
michael@0 | 8218 | |
michael@0 | 8219 | if (modDate == 0) { |
michael@0 | 8220 | // We got nothing from our attempt to ask nsIFileChannel and |
michael@0 | 8221 | // nsIHttpChannel for the last modified time. Return the current |
michael@0 | 8222 | // time. |
michael@0 | 8223 | modDate = PR_Now(); |
michael@0 | 8224 | } |
michael@0 | 8225 | |
michael@0 | 8226 | mLastModified.Truncate(); |
michael@0 | 8227 | if (modDate != 0) { |
michael@0 | 8228 | PRExplodedTime prtime; |
michael@0 | 8229 | PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime); |
michael@0 | 8230 | // "MM/DD/YYYY hh:mm:ss" |
michael@0 | 8231 | char formatedTime[24]; |
michael@0 | 8232 | if (PR_snprintf(formatedTime, sizeof(formatedTime), |
michael@0 | 8233 | "%02ld/%02ld/%04hd %02ld:%02ld:%02ld", |
michael@0 | 8234 | prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year, |
michael@0 | 8235 | prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) { |
michael@0 | 8236 | CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified); |
michael@0 | 8237 | } |
michael@0 | 8238 | } |
michael@0 | 8239 | } |
michael@0 | 8240 | |
michael@0 | 8241 | nsresult |
michael@0 | 8242 | nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix, int32_t aNamespaceID, |
michael@0 | 8243 | nsIContent **aResult) |
michael@0 | 8244 | { |
michael@0 | 8245 | #ifdef DEBUG |
michael@0 | 8246 | nsAutoString qName; |
michael@0 | 8247 | if (aPrefix) { |
michael@0 | 8248 | aPrefix->ToString(qName); |
michael@0 | 8249 | qName.Append(':'); |
michael@0 | 8250 | } |
michael@0 | 8251 | qName.Append(aName); |
michael@0 | 8252 | |
michael@0 | 8253 | // Note: "a:b:c" is a valid name in non-namespaces XML, and |
michael@0 | 8254 | // nsDocument::CreateElement can call us with such a name and no prefix, |
michael@0 | 8255 | // which would cause an error if we just used true here. |
michael@0 | 8256 | bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID(); |
michael@0 | 8257 | NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)), |
michael@0 | 8258 | "Don't pass invalid prefixes to nsDocument::CreateElem, " |
michael@0 | 8259 | "check caller."); |
michael@0 | 8260 | #endif |
michael@0 | 8261 | |
michael@0 | 8262 | *aResult = nullptr; |
michael@0 | 8263 | |
michael@0 | 8264 | nsCOMPtr<nsINodeInfo> nodeInfo; |
michael@0 | 8265 | mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID, |
michael@0 | 8266 | nsIDOMNode::ELEMENT_NODE, |
michael@0 | 8267 | getter_AddRefs(nodeInfo)); |
michael@0 | 8268 | NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 8269 | |
michael@0 | 8270 | nsCOMPtr<Element> element; |
michael@0 | 8271 | nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), |
michael@0 | 8272 | NOT_FROM_PARSER); |
michael@0 | 8273 | element.forget(aResult); |
michael@0 | 8274 | return rv; |
michael@0 | 8275 | } |
michael@0 | 8276 | |
michael@0 | 8277 | bool |
michael@0 | 8278 | nsDocument::IsSafeToFlush() const |
michael@0 | 8279 | { |
michael@0 | 8280 | nsIPresShell* shell = GetShell(); |
michael@0 | 8281 | if (!shell) |
michael@0 | 8282 | return true; |
michael@0 | 8283 | |
michael@0 | 8284 | return shell->IsSafeToFlush(); |
michael@0 | 8285 | } |
michael@0 | 8286 | |
michael@0 | 8287 | void |
michael@0 | 8288 | nsDocument::Sanitize() |
michael@0 | 8289 | { |
michael@0 | 8290 | // Sanitize the document by resetting all password fields and any form |
michael@0 | 8291 | // fields with autocomplete=off to their default values. We do this now, |
michael@0 | 8292 | // instead of when the presentation is restored, to offer some protection |
michael@0 | 8293 | // in case there is ever an exploit that allows a cached document to be |
michael@0 | 8294 | // accessed from a different document. |
michael@0 | 8295 | |
michael@0 | 8296 | // First locate all input elements, regardless of whether they are |
michael@0 | 8297 | // in a form, and reset the password and autocomplete=off elements. |
michael@0 | 8298 | |
michael@0 | 8299 | nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input")); |
michael@0 | 8300 | |
michael@0 | 8301 | nsCOMPtr<nsIContent> item; |
michael@0 | 8302 | nsAutoString value; |
michael@0 | 8303 | |
michael@0 | 8304 | uint32_t length = nodes->Length(true); |
michael@0 | 8305 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 8306 | NS_ASSERTION(nodes->Item(i), "null item in node list!"); |
michael@0 | 8307 | |
michael@0 | 8308 | nsRefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i)); |
michael@0 | 8309 | if (!input) |
michael@0 | 8310 | continue; |
michael@0 | 8311 | |
michael@0 | 8312 | bool resetValue = false; |
michael@0 | 8313 | |
michael@0 | 8314 | input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value); |
michael@0 | 8315 | if (value.LowerCaseEqualsLiteral("off")) { |
michael@0 | 8316 | resetValue = true; |
michael@0 | 8317 | } else { |
michael@0 | 8318 | input->GetType(value); |
michael@0 | 8319 | if (value.LowerCaseEqualsLiteral("password")) |
michael@0 | 8320 | resetValue = true; |
michael@0 | 8321 | } |
michael@0 | 8322 | |
michael@0 | 8323 | if (resetValue) { |
michael@0 | 8324 | input->Reset(); |
michael@0 | 8325 | } |
michael@0 | 8326 | } |
michael@0 | 8327 | |
michael@0 | 8328 | // Now locate all _form_ elements that have autocomplete=off and reset them |
michael@0 | 8329 | nodes = GetElementsByTagName(NS_LITERAL_STRING("form")); |
michael@0 | 8330 | |
michael@0 | 8331 | length = nodes->Length(true); |
michael@0 | 8332 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 8333 | NS_ASSERTION(nodes->Item(i), "null item in nodelist"); |
michael@0 | 8334 | |
michael@0 | 8335 | nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i)); |
michael@0 | 8336 | if (!form) |
michael@0 | 8337 | continue; |
michael@0 | 8338 | |
michael@0 | 8339 | nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None, |
michael@0 | 8340 | nsGkAtoms::autocomplete, value); |
michael@0 | 8341 | if (value.LowerCaseEqualsLiteral("off")) |
michael@0 | 8342 | form->Reset(); |
michael@0 | 8343 | } |
michael@0 | 8344 | } |
michael@0 | 8345 | |
michael@0 | 8346 | struct SubDocEnumArgs |
michael@0 | 8347 | { |
michael@0 | 8348 | nsIDocument::nsSubDocEnumFunc callback; |
michael@0 | 8349 | void *data; |
michael@0 | 8350 | }; |
michael@0 | 8351 | |
michael@0 | 8352 | static PLDHashOperator |
michael@0 | 8353 | SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr, |
michael@0 | 8354 | uint32_t number, void *arg) |
michael@0 | 8355 | { |
michael@0 | 8356 | SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
michael@0 | 8357 | SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg); |
michael@0 | 8358 | |
michael@0 | 8359 | nsIDocument *subdoc = entry->mSubDocument; |
michael@0 | 8360 | bool next = subdoc ? args->callback(subdoc, args->data) : true; |
michael@0 | 8361 | |
michael@0 | 8362 | return next ? PL_DHASH_NEXT : PL_DHASH_STOP; |
michael@0 | 8363 | } |
michael@0 | 8364 | |
michael@0 | 8365 | void |
michael@0 | 8366 | nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData) |
michael@0 | 8367 | { |
michael@0 | 8368 | if (mSubDocuments) { |
michael@0 | 8369 | SubDocEnumArgs args = { aCallback, aData }; |
michael@0 | 8370 | PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args); |
michael@0 | 8371 | } |
michael@0 | 8372 | } |
michael@0 | 8373 | |
michael@0 | 8374 | static PLDHashOperator |
michael@0 | 8375 | CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr, |
michael@0 | 8376 | uint32_t number, void *arg) |
michael@0 | 8377 | { |
michael@0 | 8378 | SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr); |
michael@0 | 8379 | bool *canCacheArg = static_cast<bool*>(arg); |
michael@0 | 8380 | |
michael@0 | 8381 | nsIDocument *subdoc = entry->mSubDocument; |
michael@0 | 8382 | |
michael@0 | 8383 | // The aIgnoreRequest we were passed is only for us, so don't pass it on. |
michael@0 | 8384 | bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false; |
michael@0 | 8385 | if (!canCache) { |
michael@0 | 8386 | *canCacheArg = false; |
michael@0 | 8387 | return PL_DHASH_STOP; |
michael@0 | 8388 | } |
michael@0 | 8389 | |
michael@0 | 8390 | return PL_DHASH_NEXT; |
michael@0 | 8391 | } |
michael@0 | 8392 | |
michael@0 | 8393 | #ifdef DEBUG_bryner |
michael@0 | 8394 | #define DEBUG_PAGE_CACHE |
michael@0 | 8395 | #endif |
michael@0 | 8396 | |
michael@0 | 8397 | bool |
michael@0 | 8398 | nsDocument::CanSavePresentation(nsIRequest *aNewRequest) |
michael@0 | 8399 | { |
michael@0 | 8400 | if (EventHandlingSuppressed()) { |
michael@0 | 8401 | return false; |
michael@0 | 8402 | } |
michael@0 | 8403 | |
michael@0 | 8404 | nsPIDOMWindow* win = GetInnerWindow(); |
michael@0 | 8405 | if (win && win->TimeoutSuspendCount()) { |
michael@0 | 8406 | return false; |
michael@0 | 8407 | } |
michael@0 | 8408 | |
michael@0 | 8409 | // Check our event listener manager for unload/beforeunload listeners. |
michael@0 | 8410 | nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject); |
michael@0 | 8411 | if (piTarget) { |
michael@0 | 8412 | EventListenerManager* manager = piTarget->GetExistingListenerManager(); |
michael@0 | 8413 | if (manager && manager->HasUnloadListeners()) { |
michael@0 | 8414 | return false; |
michael@0 | 8415 | } |
michael@0 | 8416 | } |
michael@0 | 8417 | |
michael@0 | 8418 | // Check if we have pending network requests |
michael@0 | 8419 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
michael@0 | 8420 | if (loadGroup) { |
michael@0 | 8421 | nsCOMPtr<nsISimpleEnumerator> requests; |
michael@0 | 8422 | loadGroup->GetRequests(getter_AddRefs(requests)); |
michael@0 | 8423 | |
michael@0 | 8424 | bool hasMore = false; |
michael@0 | 8425 | |
michael@0 | 8426 | // We want to bail out if we have any requests other than aNewRequest (or |
michael@0 | 8427 | // in the case when aNewRequest is a part of a multipart response the base |
michael@0 | 8428 | // channel the multipart response is coming in on). |
michael@0 | 8429 | nsCOMPtr<nsIChannel> baseChannel; |
michael@0 | 8430 | nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest)); |
michael@0 | 8431 | if (part) { |
michael@0 | 8432 | part->GetBaseChannel(getter_AddRefs(baseChannel)); |
michael@0 | 8433 | } |
michael@0 | 8434 | |
michael@0 | 8435 | while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { |
michael@0 | 8436 | nsCOMPtr<nsISupports> elem; |
michael@0 | 8437 | requests->GetNext(getter_AddRefs(elem)); |
michael@0 | 8438 | |
michael@0 | 8439 | nsCOMPtr<nsIRequest> request = do_QueryInterface(elem); |
michael@0 | 8440 | if (request && request != aNewRequest && request != baseChannel) { |
michael@0 | 8441 | #ifdef DEBUG_PAGE_CACHE |
michael@0 | 8442 | nsAutoCString requestName, docSpec; |
michael@0 | 8443 | request->GetName(requestName); |
michael@0 | 8444 | if (mDocumentURI) |
michael@0 | 8445 | mDocumentURI->GetSpec(docSpec); |
michael@0 | 8446 | |
michael@0 | 8447 | printf("document %s has request %s\n", |
michael@0 | 8448 | docSpec.get(), requestName.get()); |
michael@0 | 8449 | #endif |
michael@0 | 8450 | return false; |
michael@0 | 8451 | } |
michael@0 | 8452 | } |
michael@0 | 8453 | } |
michael@0 | 8454 | |
michael@0 | 8455 | // Check if we have running offline storage transactions |
michael@0 | 8456 | quota::QuotaManager* quotaManager = |
michael@0 | 8457 | win ? quota::QuotaManager::Get() : nullptr; |
michael@0 | 8458 | if (quotaManager && quotaManager->HasOpenTransactions(win)) { |
michael@0 | 8459 | return false; |
michael@0 | 8460 | } |
michael@0 | 8461 | |
michael@0 | 8462 | #ifdef MOZ_MEDIA_NAVIGATOR |
michael@0 | 8463 | // Check if we have active GetUserMedia use |
michael@0 | 8464 | if (MediaManager::Exists() && win && |
michael@0 | 8465 | MediaManager::Get()->IsWindowStillActive(win->WindowID())) { |
michael@0 | 8466 | return false; |
michael@0 | 8467 | } |
michael@0 | 8468 | #endif // MOZ_MEDIA_NAVIGATOR |
michael@0 | 8469 | |
michael@0 | 8470 | #ifdef MOZ_WEBRTC |
michael@0 | 8471 | // Check if we have active PeerConnections |
michael@0 | 8472 | nsCOMPtr<IPeerConnectionManager> pcManager = |
michael@0 | 8473 | do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID); |
michael@0 | 8474 | |
michael@0 | 8475 | if (pcManager && win) { |
michael@0 | 8476 | bool active; |
michael@0 | 8477 | pcManager->HasActivePeerConnection(win->WindowID(), &active); |
michael@0 | 8478 | if (active) { |
michael@0 | 8479 | return false; |
michael@0 | 8480 | } |
michael@0 | 8481 | } |
michael@0 | 8482 | #endif // MOZ_WEBRTC |
michael@0 | 8483 | |
michael@0 | 8484 | bool canCache = true; |
michael@0 | 8485 | if (mSubDocuments) |
michael@0 | 8486 | PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache); |
michael@0 | 8487 | |
michael@0 | 8488 | return canCache; |
michael@0 | 8489 | } |
michael@0 | 8490 | |
michael@0 | 8491 | void |
michael@0 | 8492 | nsDocument::Destroy() |
michael@0 | 8493 | { |
michael@0 | 8494 | // The ContentViewer wants to release the document now. So, tell our content |
michael@0 | 8495 | // to drop any references to the document so that it can be destroyed. |
michael@0 | 8496 | if (mIsGoingAway) |
michael@0 | 8497 | return; |
michael@0 | 8498 | |
michael@0 | 8499 | mIsGoingAway = true; |
michael@0 | 8500 | |
michael@0 | 8501 | RemovedFromDocShell(); |
michael@0 | 8502 | |
michael@0 | 8503 | bool oldVal = mInUnlinkOrDeletion; |
michael@0 | 8504 | mInUnlinkOrDeletion = true; |
michael@0 | 8505 | uint32_t i, count = mChildren.ChildCount(); |
michael@0 | 8506 | for (i = 0; i < count; ++i) { |
michael@0 | 8507 | mChildren.ChildAt(i)->DestroyContent(); |
michael@0 | 8508 | } |
michael@0 | 8509 | mInUnlinkOrDeletion = oldVal; |
michael@0 | 8510 | |
michael@0 | 8511 | mLayoutHistoryState = nullptr; |
michael@0 | 8512 | |
michael@0 | 8513 | // Shut down our external resource map. We might not need this for |
michael@0 | 8514 | // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but |
michael@0 | 8515 | // tearing down all those frame trees right now is the right thing to do. |
michael@0 | 8516 | mExternalResourceMap.Shutdown(); |
michael@0 | 8517 | |
michael@0 | 8518 | mRegistry = nullptr; |
michael@0 | 8519 | |
michael@0 | 8520 | // XXX We really should let cycle collection do this, but that currently still |
michael@0 | 8521 | // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). |
michael@0 | 8522 | ReleaseWrapper(static_cast<nsINode*>(this)); |
michael@0 | 8523 | } |
michael@0 | 8524 | |
michael@0 | 8525 | void |
michael@0 | 8526 | nsDocument::RemovedFromDocShell() |
michael@0 | 8527 | { |
michael@0 | 8528 | if (mRemovedFromDocShell) |
michael@0 | 8529 | return; |
michael@0 | 8530 | |
michael@0 | 8531 | mRemovedFromDocShell = true; |
michael@0 | 8532 | EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
michael@0 | 8533 | |
michael@0 | 8534 | uint32_t i, count = mChildren.ChildCount(); |
michael@0 | 8535 | for (i = 0; i < count; ++i) { |
michael@0 | 8536 | mChildren.ChildAt(i)->SaveSubtreeState(); |
michael@0 | 8537 | } |
michael@0 | 8538 | } |
michael@0 | 8539 | |
michael@0 | 8540 | already_AddRefed<nsILayoutHistoryState> |
michael@0 | 8541 | nsDocument::GetLayoutHistoryState() const |
michael@0 | 8542 | { |
michael@0 | 8543 | nsCOMPtr<nsILayoutHistoryState> state; |
michael@0 | 8544 | if (!mScriptGlobalObject) { |
michael@0 | 8545 | state = mLayoutHistoryState; |
michael@0 | 8546 | } else { |
michael@0 | 8547 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 8548 | if (docShell) { |
michael@0 | 8549 | docShell->GetLayoutHistoryState(getter_AddRefs(state)); |
michael@0 | 8550 | } |
michael@0 | 8551 | } |
michael@0 | 8552 | |
michael@0 | 8553 | return state.forget(); |
michael@0 | 8554 | } |
michael@0 | 8555 | |
michael@0 | 8556 | void |
michael@0 | 8557 | nsDocument::EnsureOnloadBlocker() |
michael@0 | 8558 | { |
michael@0 | 8559 | // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
michael@0 | 8560 | // -- it's not ours. |
michael@0 | 8561 | if (mOnloadBlockCount != 0 && mScriptGlobalObject) { |
michael@0 | 8562 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
michael@0 | 8563 | if (loadGroup) { |
michael@0 | 8564 | // Check first to see if mOnloadBlocker is in the loadgroup. |
michael@0 | 8565 | nsCOMPtr<nsISimpleEnumerator> requests; |
michael@0 | 8566 | loadGroup->GetRequests(getter_AddRefs(requests)); |
michael@0 | 8567 | |
michael@0 | 8568 | bool hasMore = false; |
michael@0 | 8569 | while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { |
michael@0 | 8570 | nsCOMPtr<nsISupports> elem; |
michael@0 | 8571 | requests->GetNext(getter_AddRefs(elem)); |
michael@0 | 8572 | nsCOMPtr<nsIRequest> request = do_QueryInterface(elem); |
michael@0 | 8573 | if (request && request == mOnloadBlocker) { |
michael@0 | 8574 | return; |
michael@0 | 8575 | } |
michael@0 | 8576 | } |
michael@0 | 8577 | |
michael@0 | 8578 | // Not in the loadgroup, so add it. |
michael@0 | 8579 | loadGroup->AddRequest(mOnloadBlocker, nullptr); |
michael@0 | 8580 | } |
michael@0 | 8581 | } |
michael@0 | 8582 | } |
michael@0 | 8583 | |
michael@0 | 8584 | void |
michael@0 | 8585 | nsDocument::AsyncBlockOnload() |
michael@0 | 8586 | { |
michael@0 | 8587 | while (mAsyncOnloadBlockCount) { |
michael@0 | 8588 | --mAsyncOnloadBlockCount; |
michael@0 | 8589 | BlockOnload(); |
michael@0 | 8590 | } |
michael@0 | 8591 | } |
michael@0 | 8592 | |
michael@0 | 8593 | void |
michael@0 | 8594 | nsDocument::BlockOnload() |
michael@0 | 8595 | { |
michael@0 | 8596 | if (mDisplayDocument) { |
michael@0 | 8597 | mDisplayDocument->BlockOnload(); |
michael@0 | 8598 | return; |
michael@0 | 8599 | } |
michael@0 | 8600 | |
michael@0 | 8601 | // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
michael@0 | 8602 | // -- it's not ours. |
michael@0 | 8603 | if (mOnloadBlockCount == 0 && mScriptGlobalObject) { |
michael@0 | 8604 | if (!nsContentUtils::IsSafeToRunScript()) { |
michael@0 | 8605 | // Because AddRequest may lead to OnStateChange calls in chrome, |
michael@0 | 8606 | // block onload only when there are no script blockers. |
michael@0 | 8607 | ++mAsyncOnloadBlockCount; |
michael@0 | 8608 | if (mAsyncOnloadBlockCount == 1) { |
michael@0 | 8609 | bool success = nsContentUtils::AddScriptRunner( |
michael@0 | 8610 | NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload)); |
michael@0 | 8611 | |
michael@0 | 8612 | // The script runner shouldn't fail to add. But if somebody broke |
michael@0 | 8613 | // something and it does, we'll thrash at 100% cpu forever. The best |
michael@0 | 8614 | // response is just to ignore the onload blocking request. See bug 579535. |
michael@0 | 8615 | if (!success) { |
michael@0 | 8616 | NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!"); |
michael@0 | 8617 | mAsyncOnloadBlockCount = 0; |
michael@0 | 8618 | } |
michael@0 | 8619 | } |
michael@0 | 8620 | return; |
michael@0 | 8621 | } |
michael@0 | 8622 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
michael@0 | 8623 | if (loadGroup) { |
michael@0 | 8624 | loadGroup->AddRequest(mOnloadBlocker, nullptr); |
michael@0 | 8625 | } |
michael@0 | 8626 | } |
michael@0 | 8627 | ++mOnloadBlockCount; |
michael@0 | 8628 | } |
michael@0 | 8629 | |
michael@0 | 8630 | void |
michael@0 | 8631 | nsDocument::UnblockOnload(bool aFireSync) |
michael@0 | 8632 | { |
michael@0 | 8633 | if (mDisplayDocument) { |
michael@0 | 8634 | mDisplayDocument->UnblockOnload(aFireSync); |
michael@0 | 8635 | return; |
michael@0 | 8636 | } |
michael@0 | 8637 | |
michael@0 | 8638 | if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) { |
michael@0 | 8639 | NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call"); |
michael@0 | 8640 | return; |
michael@0 | 8641 | } |
michael@0 | 8642 | |
michael@0 | 8643 | --mOnloadBlockCount; |
michael@0 | 8644 | |
michael@0 | 8645 | if (mOnloadBlockCount == 0) { |
michael@0 | 8646 | if (mScriptGlobalObject) { |
michael@0 | 8647 | // Only manipulate the loadgroup in this case, because if mScriptGlobalObject |
michael@0 | 8648 | // is null, it's not ours. |
michael@0 | 8649 | if (aFireSync && mAsyncOnloadBlockCount == 0) { |
michael@0 | 8650 | // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it |
michael@0 | 8651 | ++mOnloadBlockCount; |
michael@0 | 8652 | DoUnblockOnload(); |
michael@0 | 8653 | } else { |
michael@0 | 8654 | PostUnblockOnloadEvent(); |
michael@0 | 8655 | } |
michael@0 | 8656 | } else if (mIsBeingUsedAsImage) { |
michael@0 | 8657 | // To correctly unblock onload for a document that contains an SVG |
michael@0 | 8658 | // image, we need to know when all of the SVG document's resources are |
michael@0 | 8659 | // done loading, in a way comparable to |window.onload|. We fire this |
michael@0 | 8660 | // event to indicate that the SVG should be considered fully loaded. |
michael@0 | 8661 | // Because scripting is disabled on SVG-as-image documents, this event |
michael@0 | 8662 | // is not accessible to content authors. (See bug 837135.) |
michael@0 | 8663 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 8664 | new AsyncEventDispatcher(this, |
michael@0 | 8665 | NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), |
michael@0 | 8666 | false, |
michael@0 | 8667 | false); |
michael@0 | 8668 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 8669 | } |
michael@0 | 8670 | } |
michael@0 | 8671 | } |
michael@0 | 8672 | |
michael@0 | 8673 | class nsUnblockOnloadEvent : public nsRunnable { |
michael@0 | 8674 | public: |
michael@0 | 8675 | nsUnblockOnloadEvent(nsDocument *doc) : mDoc(doc) {} |
michael@0 | 8676 | NS_IMETHOD Run() { |
michael@0 | 8677 | mDoc->DoUnblockOnload(); |
michael@0 | 8678 | return NS_OK; |
michael@0 | 8679 | } |
michael@0 | 8680 | private: |
michael@0 | 8681 | nsRefPtr<nsDocument> mDoc; |
michael@0 | 8682 | }; |
michael@0 | 8683 | |
michael@0 | 8684 | void |
michael@0 | 8685 | nsDocument::PostUnblockOnloadEvent() |
michael@0 | 8686 | { |
michael@0 | 8687 | nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this); |
michael@0 | 8688 | nsresult rv = NS_DispatchToCurrentThread(evt); |
michael@0 | 8689 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 8690 | // Stabilize block count so we don't post more events while this one is up |
michael@0 | 8691 | ++mOnloadBlockCount; |
michael@0 | 8692 | } else { |
michael@0 | 8693 | NS_WARNING("failed to dispatch nsUnblockOnloadEvent"); |
michael@0 | 8694 | } |
michael@0 | 8695 | } |
michael@0 | 8696 | |
michael@0 | 8697 | void |
michael@0 | 8698 | nsDocument::DoUnblockOnload() |
michael@0 | 8699 | { |
michael@0 | 8700 | NS_PRECONDITION(!mDisplayDocument, |
michael@0 | 8701 | "Shouldn't get here for resource document"); |
michael@0 | 8702 | NS_PRECONDITION(mOnloadBlockCount != 0, |
michael@0 | 8703 | "Shouldn't have a count of zero here, since we stabilized in " |
michael@0 | 8704 | "PostUnblockOnloadEvent"); |
michael@0 | 8705 | |
michael@0 | 8706 | --mOnloadBlockCount; |
michael@0 | 8707 | |
michael@0 | 8708 | if (mOnloadBlockCount != 0) { |
michael@0 | 8709 | // We blocked again after the last unblock. Nothing to do here. We'll |
michael@0 | 8710 | // post a new event when we unblock again. |
michael@0 | 8711 | return; |
michael@0 | 8712 | } |
michael@0 | 8713 | |
michael@0 | 8714 | if (mAsyncOnloadBlockCount != 0) { |
michael@0 | 8715 | // We need to wait until the async onload block has been handled. |
michael@0 | 8716 | PostUnblockOnloadEvent(); |
michael@0 | 8717 | } |
michael@0 | 8718 | |
michael@0 | 8719 | // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup |
michael@0 | 8720 | // -- it's not ours. |
michael@0 | 8721 | if (mScriptGlobalObject) { |
michael@0 | 8722 | nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup(); |
michael@0 | 8723 | if (loadGroup) { |
michael@0 | 8724 | loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); |
michael@0 | 8725 | } |
michael@0 | 8726 | } |
michael@0 | 8727 | } |
michael@0 | 8728 | |
michael@0 | 8729 | nsIContent* |
michael@0 | 8730 | nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const |
michael@0 | 8731 | { |
michael@0 | 8732 | for (nsIFrame* f = aFrame; f; |
michael@0 | 8733 | f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) { |
michael@0 | 8734 | nsIContent* content = f->GetContent(); |
michael@0 | 8735 | if (!content || content->IsInAnonymousSubtree()) |
michael@0 | 8736 | continue; |
michael@0 | 8737 | |
michael@0 | 8738 | if (content->OwnerDoc() == this) { |
michael@0 | 8739 | return content; |
michael@0 | 8740 | } |
michael@0 | 8741 | // We must be in a subdocument so jump directly to the root frame. |
michael@0 | 8742 | // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to |
michael@0 | 8743 | // the containing document. |
michael@0 | 8744 | f = f->PresContext()->GetPresShell()->GetRootFrame(); |
michael@0 | 8745 | } |
michael@0 | 8746 | |
michael@0 | 8747 | return nullptr; |
michael@0 | 8748 | } |
michael@0 | 8749 | |
michael@0 | 8750 | void |
michael@0 | 8751 | nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget, |
michael@0 | 8752 | const nsAString& aType, |
michael@0 | 8753 | bool aPersisted) |
michael@0 | 8754 | { |
michael@0 | 8755 | if (aDispatchTarget) { |
michael@0 | 8756 | nsCOMPtr<nsIDOMEvent> event; |
michael@0 | 8757 | CreateEvent(NS_LITERAL_STRING("pagetransition"), getter_AddRefs(event)); |
michael@0 | 8758 | nsCOMPtr<nsIDOMPageTransitionEvent> ptEvent = do_QueryInterface(event); |
michael@0 | 8759 | if (ptEvent && NS_SUCCEEDED(ptEvent->InitPageTransitionEvent(aType, true, |
michael@0 | 8760 | true, |
michael@0 | 8761 | aPersisted))) { |
michael@0 | 8762 | event->SetTrusted(true); |
michael@0 | 8763 | event->SetTarget(this); |
michael@0 | 8764 | EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, |
michael@0 | 8765 | nullptr, nullptr); |
michael@0 | 8766 | } |
michael@0 | 8767 | } |
michael@0 | 8768 | } |
michael@0 | 8769 | |
michael@0 | 8770 | static bool |
michael@0 | 8771 | NotifyPageShow(nsIDocument* aDocument, void* aData) |
michael@0 | 8772 | { |
michael@0 | 8773 | const bool* aPersistedPtr = static_cast<const bool*>(aData); |
michael@0 | 8774 | aDocument->OnPageShow(*aPersistedPtr, nullptr); |
michael@0 | 8775 | return true; |
michael@0 | 8776 | } |
michael@0 | 8777 | |
michael@0 | 8778 | void |
michael@0 | 8779 | nsDocument::OnPageShow(bool aPersisted, |
michael@0 | 8780 | EventTarget* aDispatchStartTarget) |
michael@0 | 8781 | { |
michael@0 | 8782 | mVisible = true; |
michael@0 | 8783 | |
michael@0 | 8784 | EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
michael@0 | 8785 | EnumerateExternalResources(NotifyPageShow, &aPersisted); |
michael@0 | 8786 | |
michael@0 | 8787 | Element* root = GetRootElement(); |
michael@0 | 8788 | if (aPersisted && root) { |
michael@0 | 8789 | // Send out notifications that our <link> elements are attached. |
michael@0 | 8790 | nsRefPtr<nsContentList> links = NS_GetContentList(root, |
michael@0 | 8791 | kNameSpaceID_XHTML, |
michael@0 | 8792 | NS_LITERAL_STRING("link")); |
michael@0 | 8793 | |
michael@0 | 8794 | uint32_t linkCount = links->Length(true); |
michael@0 | 8795 | for (uint32_t i = 0; i < linkCount; ++i) { |
michael@0 | 8796 | static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded(); |
michael@0 | 8797 | } |
michael@0 | 8798 | } |
michael@0 | 8799 | |
michael@0 | 8800 | // See nsIDocument |
michael@0 | 8801 | if (!aDispatchStartTarget) { |
michael@0 | 8802 | // Set mIsShowing before firing events, in case those event handlers |
michael@0 | 8803 | // move us around. |
michael@0 | 8804 | mIsShowing = true; |
michael@0 | 8805 | } |
michael@0 | 8806 | |
michael@0 | 8807 | if (mAnimationController) { |
michael@0 | 8808 | mAnimationController->OnPageShow(); |
michael@0 | 8809 | } |
michael@0 | 8810 | |
michael@0 | 8811 | if (aPersisted) { |
michael@0 | 8812 | SetImagesNeedAnimating(true); |
michael@0 | 8813 | } |
michael@0 | 8814 | |
michael@0 | 8815 | UpdateVisibilityState(); |
michael@0 | 8816 | |
michael@0 | 8817 | nsCOMPtr<EventTarget> target = aDispatchStartTarget; |
michael@0 | 8818 | if (!target) { |
michael@0 | 8819 | target = do_QueryInterface(GetWindow()); |
michael@0 | 8820 | } |
michael@0 | 8821 | |
michael@0 | 8822 | // Dispatch observer notification to notify observers page is shown. |
michael@0 | 8823 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 8824 | nsIPrincipal *principal = GetPrincipal(); |
michael@0 | 8825 | os->NotifyObservers(static_cast<nsIDocument*>(this), |
michael@0 | 8826 | nsContentUtils::IsSystemPrincipal(principal) ? |
michael@0 | 8827 | "chrome-page-shown" : |
michael@0 | 8828 | "content-page-shown", |
michael@0 | 8829 | nullptr); |
michael@0 | 8830 | |
michael@0 | 8831 | |
michael@0 | 8832 | DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted); |
michael@0 | 8833 | } |
michael@0 | 8834 | |
michael@0 | 8835 | static bool |
michael@0 | 8836 | NotifyPageHide(nsIDocument* aDocument, void* aData) |
michael@0 | 8837 | { |
michael@0 | 8838 | const bool* aPersistedPtr = static_cast<const bool*>(aData); |
michael@0 | 8839 | aDocument->OnPageHide(*aPersistedPtr, nullptr); |
michael@0 | 8840 | return true; |
michael@0 | 8841 | } |
michael@0 | 8842 | |
michael@0 | 8843 | static void |
michael@0 | 8844 | DispatchFullScreenChange(nsIDocument* aTarget) |
michael@0 | 8845 | { |
michael@0 | 8846 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 8847 | new AsyncEventDispatcher(aTarget, |
michael@0 | 8848 | NS_LITERAL_STRING("mozfullscreenchange"), |
michael@0 | 8849 | true, |
michael@0 | 8850 | false); |
michael@0 | 8851 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 8852 | } |
michael@0 | 8853 | |
michael@0 | 8854 | void |
michael@0 | 8855 | nsDocument::OnPageHide(bool aPersisted, |
michael@0 | 8856 | EventTarget* aDispatchStartTarget) |
michael@0 | 8857 | { |
michael@0 | 8858 | // Send out notifications that our <link> elements are detached, |
michael@0 | 8859 | // but only if this is not a full unload. |
michael@0 | 8860 | Element* root = GetRootElement(); |
michael@0 | 8861 | if (aPersisted && root) { |
michael@0 | 8862 | nsRefPtr<nsContentList> links = NS_GetContentList(root, |
michael@0 | 8863 | kNameSpaceID_XHTML, |
michael@0 | 8864 | NS_LITERAL_STRING("link")); |
michael@0 | 8865 | |
michael@0 | 8866 | uint32_t linkCount = links->Length(true); |
michael@0 | 8867 | for (uint32_t i = 0; i < linkCount; ++i) { |
michael@0 | 8868 | static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved(); |
michael@0 | 8869 | } |
michael@0 | 8870 | } |
michael@0 | 8871 | |
michael@0 | 8872 | // See nsIDocument |
michael@0 | 8873 | if (!aDispatchStartTarget) { |
michael@0 | 8874 | // Set mIsShowing before firing events, in case those event handlers |
michael@0 | 8875 | // move us around. |
michael@0 | 8876 | mIsShowing = false; |
michael@0 | 8877 | } |
michael@0 | 8878 | |
michael@0 | 8879 | if (mAnimationController) { |
michael@0 | 8880 | mAnimationController->OnPageHide(); |
michael@0 | 8881 | } |
michael@0 | 8882 | |
michael@0 | 8883 | if (aPersisted) { |
michael@0 | 8884 | SetImagesNeedAnimating(false); |
michael@0 | 8885 | } |
michael@0 | 8886 | |
michael@0 | 8887 | MozExitPointerLock(); |
michael@0 | 8888 | |
michael@0 | 8889 | // Now send out a PageHide event. |
michael@0 | 8890 | nsCOMPtr<EventTarget> target = aDispatchStartTarget; |
michael@0 | 8891 | if (!target) { |
michael@0 | 8892 | target = do_QueryInterface(GetWindow()); |
michael@0 | 8893 | } |
michael@0 | 8894 | |
michael@0 | 8895 | // Dispatch observer notification to notify observers page is hidden. |
michael@0 | 8896 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 8897 | nsIPrincipal *principal = GetPrincipal(); |
michael@0 | 8898 | os->NotifyObservers(static_cast<nsIDocument*>(this), |
michael@0 | 8899 | nsContentUtils::IsSystemPrincipal(principal) ? |
michael@0 | 8900 | "chrome-page-hidden" : |
michael@0 | 8901 | "content-page-hidden", |
michael@0 | 8902 | nullptr); |
michael@0 | 8903 | |
michael@0 | 8904 | DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted); |
michael@0 | 8905 | |
michael@0 | 8906 | mVisible = false; |
michael@0 | 8907 | |
michael@0 | 8908 | UpdateVisibilityState(); |
michael@0 | 8909 | |
michael@0 | 8910 | EnumerateExternalResources(NotifyPageHide, &aPersisted); |
michael@0 | 8911 | EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
michael@0 | 8912 | |
michael@0 | 8913 | if (IsFullScreenDoc()) { |
michael@0 | 8914 | // If this document was fullscreen, we should exit fullscreen in this |
michael@0 | 8915 | // doctree branch. This ensures that if the user navigates while in |
michael@0 | 8916 | // fullscreen mode we don't leave its still visible ancestor documents |
michael@0 | 8917 | // in fullscreen mode. So exit fullscreen in the document's fullscreen |
michael@0 | 8918 | // root document, as this will exit fullscreen in all the root's |
michael@0 | 8919 | // descendant documents. Note that documents are removed from the |
michael@0 | 8920 | // doctree by the time OnPageHide() is called, so we must store a |
michael@0 | 8921 | // reference to the root (in nsDocument::mFullscreenRoot) since we can't |
michael@0 | 8922 | // just traverse the doctree to get the root. |
michael@0 | 8923 | nsIDocument::ExitFullscreen(this, /* async */ false); |
michael@0 | 8924 | |
michael@0 | 8925 | // Since the document is removed from the doctree before OnPageHide() is |
michael@0 | 8926 | // called, ExitFullscreen() can't traverse from the root down to *this* |
michael@0 | 8927 | // document, so we must manually call CleanupFullscreenState() below too. |
michael@0 | 8928 | // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot, |
michael@0 | 8929 | // so we *must* call it after ExitFullscreen(), not before. |
michael@0 | 8930 | // OnPageHide() is called in every hidden (i.e. descendant) document, |
michael@0 | 8931 | // so calling CleanupFullscreenState() here will ensure all hidden |
michael@0 | 8932 | // documents have their fullscreen state reset. |
michael@0 | 8933 | CleanupFullscreenState(); |
michael@0 | 8934 | |
michael@0 | 8935 | // If anyone was listening to this document's state, advertizing the state |
michael@0 | 8936 | // change would be the least of the politeness. |
michael@0 | 8937 | DispatchFullScreenChange(this); |
michael@0 | 8938 | } |
michael@0 | 8939 | } |
michael@0 | 8940 | |
michael@0 | 8941 | void |
michael@0 | 8942 | nsDocument::WillDispatchMutationEvent(nsINode* aTarget) |
michael@0 | 8943 | { |
michael@0 | 8944 | NS_ASSERTION(mSubtreeModifiedDepth != 0 || |
michael@0 | 8945 | mSubtreeModifiedTargets.Count() == 0, |
michael@0 | 8946 | "mSubtreeModifiedTargets not cleared after dispatching?"); |
michael@0 | 8947 | ++mSubtreeModifiedDepth; |
michael@0 | 8948 | if (aTarget) { |
michael@0 | 8949 | // MayDispatchMutationEvent is often called just before this method, |
michael@0 | 8950 | // so it has already appended the node to mSubtreeModifiedTargets. |
michael@0 | 8951 | int32_t count = mSubtreeModifiedTargets.Count(); |
michael@0 | 8952 | if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) { |
michael@0 | 8953 | mSubtreeModifiedTargets.AppendObject(aTarget); |
michael@0 | 8954 | } |
michael@0 | 8955 | } |
michael@0 | 8956 | } |
michael@0 | 8957 | |
michael@0 | 8958 | void |
michael@0 | 8959 | nsDocument::MutationEventDispatched(nsINode* aTarget) |
michael@0 | 8960 | { |
michael@0 | 8961 | --mSubtreeModifiedDepth; |
michael@0 | 8962 | if (mSubtreeModifiedDepth == 0) { |
michael@0 | 8963 | int32_t count = mSubtreeModifiedTargets.Count(); |
michael@0 | 8964 | if (!count) { |
michael@0 | 8965 | return; |
michael@0 | 8966 | } |
michael@0 | 8967 | |
michael@0 | 8968 | nsCOMPtr<nsPIDOMWindow> window; |
michael@0 | 8969 | window = do_QueryInterface(GetWindow()); |
michael@0 | 8970 | if (window && |
michael@0 | 8971 | !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) { |
michael@0 | 8972 | mSubtreeModifiedTargets.Clear(); |
michael@0 | 8973 | return; |
michael@0 | 8974 | } |
michael@0 | 8975 | |
michael@0 | 8976 | nsCOMArray<nsINode> realTargets; |
michael@0 | 8977 | for (int32_t i = 0; i < count; ++i) { |
michael@0 | 8978 | nsINode* possibleTarget = mSubtreeModifiedTargets[i]; |
michael@0 | 8979 | nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget); |
michael@0 | 8980 | if (content && content->ChromeOnlyAccess()) { |
michael@0 | 8981 | continue; |
michael@0 | 8982 | } |
michael@0 | 8983 | |
michael@0 | 8984 | nsINode* commonAncestor = nullptr; |
michael@0 | 8985 | int32_t realTargetCount = realTargets.Count(); |
michael@0 | 8986 | for (int32_t j = 0; j < realTargetCount; ++j) { |
michael@0 | 8987 | commonAncestor = |
michael@0 | 8988 | nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]); |
michael@0 | 8989 | if (commonAncestor) { |
michael@0 | 8990 | realTargets.ReplaceObjectAt(commonAncestor, j); |
michael@0 | 8991 | break; |
michael@0 | 8992 | } |
michael@0 | 8993 | } |
michael@0 | 8994 | if (!commonAncestor) { |
michael@0 | 8995 | realTargets.AppendObject(possibleTarget); |
michael@0 | 8996 | } |
michael@0 | 8997 | } |
michael@0 | 8998 | |
michael@0 | 8999 | mSubtreeModifiedTargets.Clear(); |
michael@0 | 9000 | |
michael@0 | 9001 | int32_t realTargetCount = realTargets.Count(); |
michael@0 | 9002 | for (int32_t k = 0; k < realTargetCount; ++k) { |
michael@0 | 9003 | InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED); |
michael@0 | 9004 | (new AsyncEventDispatcher(realTargets[k], mutation))-> |
michael@0 | 9005 | RunDOMEventWhenSafe(); |
michael@0 | 9006 | } |
michael@0 | 9007 | } |
michael@0 | 9008 | } |
michael@0 | 9009 | |
michael@0 | 9010 | void |
michael@0 | 9011 | nsDocument::AddStyleRelevantLink(Link* aLink) |
michael@0 | 9012 | { |
michael@0 | 9013 | NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!"); |
michael@0 | 9014 | #ifdef DEBUG |
michael@0 | 9015 | nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink); |
michael@0 | 9016 | NS_ASSERTION(!entry, "Document already knows about this Link!"); |
michael@0 | 9017 | mStyledLinksCleared = false; |
michael@0 | 9018 | #endif |
michael@0 | 9019 | (void)mStyledLinks.PutEntry(aLink); |
michael@0 | 9020 | } |
michael@0 | 9021 | |
michael@0 | 9022 | void |
michael@0 | 9023 | nsDocument::ForgetLink(Link* aLink) |
michael@0 | 9024 | { |
michael@0 | 9025 | NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!"); |
michael@0 | 9026 | #ifdef DEBUG |
michael@0 | 9027 | nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink); |
michael@0 | 9028 | NS_ASSERTION(entry || mStyledLinksCleared, |
michael@0 | 9029 | "Document knows nothing about this Link!"); |
michael@0 | 9030 | #endif |
michael@0 | 9031 | (void)mStyledLinks.RemoveEntry(aLink); |
michael@0 | 9032 | } |
michael@0 | 9033 | |
michael@0 | 9034 | void |
michael@0 | 9035 | nsDocument::DestroyElementMaps() |
michael@0 | 9036 | { |
michael@0 | 9037 | #ifdef DEBUG |
michael@0 | 9038 | mStyledLinksCleared = true; |
michael@0 | 9039 | #endif |
michael@0 | 9040 | mStyledLinks.Clear(); |
michael@0 | 9041 | mIdentifierMap.Clear(); |
michael@0 | 9042 | ++mExpandoAndGeneration.generation; |
michael@0 | 9043 | } |
michael@0 | 9044 | |
michael@0 | 9045 | static |
michael@0 | 9046 | PLDHashOperator |
michael@0 | 9047 | EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray) |
michael@0 | 9048 | { |
michael@0 | 9049 | LinkArray* array = static_cast<LinkArray*>(aArray); |
michael@0 | 9050 | (void)array->AppendElement(aEntry->GetKey()); |
michael@0 | 9051 | return PL_DHASH_NEXT; |
michael@0 | 9052 | } |
michael@0 | 9053 | |
michael@0 | 9054 | void |
michael@0 | 9055 | nsDocument::RefreshLinkHrefs() |
michael@0 | 9056 | { |
michael@0 | 9057 | // Get a list of all links we know about. We will reset them, which will |
michael@0 | 9058 | // remove them from the document, so we need a copy of what is in the |
michael@0 | 9059 | // hashtable. |
michael@0 | 9060 | LinkArray linksToNotify(mStyledLinks.Count()); |
michael@0 | 9061 | (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify); |
michael@0 | 9062 | |
michael@0 | 9063 | // Reset all of our styled links. |
michael@0 | 9064 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 9065 | for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) { |
michael@0 | 9066 | linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref()); |
michael@0 | 9067 | } |
michael@0 | 9068 | } |
michael@0 | 9069 | |
michael@0 | 9070 | nsresult |
michael@0 | 9071 | nsDocument::CloneDocHelper(nsDocument* clone) const |
michael@0 | 9072 | { |
michael@0 | 9073 | clone->mIsStaticDocument = mCreatingStaticClone; |
michael@0 | 9074 | |
michael@0 | 9075 | // Init document |
michael@0 | 9076 | nsresult rv = clone->Init(); |
michael@0 | 9077 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9078 | |
michael@0 | 9079 | // Set URI/principal |
michael@0 | 9080 | clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI()); |
michael@0 | 9081 | clone->SetChromeXHRDocURI(mChromeXHRDocURI); |
michael@0 | 9082 | // Must set the principal first, since SetBaseURI checks it. |
michael@0 | 9083 | clone->SetPrincipal(NodePrincipal()); |
michael@0 | 9084 | clone->mDocumentBaseURI = mDocumentBaseURI; |
michael@0 | 9085 | clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI); |
michael@0 | 9086 | |
michael@0 | 9087 | if (mCreatingStaticClone) { |
michael@0 | 9088 | nsCOMPtr<nsILoadGroup> loadGroup; |
michael@0 | 9089 | |
michael@0 | 9090 | // |mDocumentContainer| is the container of the document that is being |
michael@0 | 9091 | // created and not the original container. See CreateStaticClone function(). |
michael@0 | 9092 | nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer); |
michael@0 | 9093 | if (docLoader) { |
michael@0 | 9094 | docLoader->GetLoadGroup(getter_AddRefs(loadGroup)); |
michael@0 | 9095 | } |
michael@0 | 9096 | nsCOMPtr<nsIChannel> channel = GetChannel(); |
michael@0 | 9097 | if (channel && loadGroup) { |
michael@0 | 9098 | clone->Reset(channel, loadGroup); |
michael@0 | 9099 | } else { |
michael@0 | 9100 | nsIURI* uri = static_cast<const nsIDocument*>(this)->GetDocumentURI(); |
michael@0 | 9101 | if (uri) { |
michael@0 | 9102 | clone->ResetToURI(uri, loadGroup, NodePrincipal()); |
michael@0 | 9103 | } |
michael@0 | 9104 | } |
michael@0 | 9105 | clone->SetContainer(mDocumentContainer); |
michael@0 | 9106 | } |
michael@0 | 9107 | |
michael@0 | 9108 | // Set scripting object |
michael@0 | 9109 | bool hasHadScriptObject = true; |
michael@0 | 9110 | nsIScriptGlobalObject* scriptObject = |
michael@0 | 9111 | GetScriptHandlingObject(hasHadScriptObject); |
michael@0 | 9112 | NS_ENSURE_STATE(scriptObject || !hasHadScriptObject); |
michael@0 | 9113 | if (scriptObject) { |
michael@0 | 9114 | clone->SetScriptHandlingObject(scriptObject); |
michael@0 | 9115 | } else { |
michael@0 | 9116 | clone->SetScopeObject(GetScopeObject()); |
michael@0 | 9117 | } |
michael@0 | 9118 | // Make the clone a data document |
michael@0 | 9119 | clone->SetLoadedAsData(true); |
michael@0 | 9120 | |
michael@0 | 9121 | // Misc state |
michael@0 | 9122 | |
michael@0 | 9123 | // State from nsIDocument |
michael@0 | 9124 | clone->mCharacterSet = mCharacterSet; |
michael@0 | 9125 | clone->mCharacterSetSource = mCharacterSetSource; |
michael@0 | 9126 | clone->mCompatMode = mCompatMode; |
michael@0 | 9127 | clone->mBidiOptions = mBidiOptions; |
michael@0 | 9128 | clone->mContentLanguage = mContentLanguage; |
michael@0 | 9129 | clone->SetContentTypeInternal(GetContentTypeInternal()); |
michael@0 | 9130 | clone->mSecurityInfo = mSecurityInfo; |
michael@0 | 9131 | |
michael@0 | 9132 | // State from nsDocument |
michael@0 | 9133 | clone->mIsRegularHTML = mIsRegularHTML; |
michael@0 | 9134 | clone->mXMLDeclarationBits = mXMLDeclarationBits; |
michael@0 | 9135 | clone->mBaseTarget = mBaseTarget; |
michael@0 | 9136 | return NS_OK; |
michael@0 | 9137 | } |
michael@0 | 9138 | |
michael@0 | 9139 | void |
michael@0 | 9140 | nsDocument::SetReadyStateInternal(ReadyState rs) |
michael@0 | 9141 | { |
michael@0 | 9142 | mReadyState = rs; |
michael@0 | 9143 | if (rs == READYSTATE_UNINITIALIZED) { |
michael@0 | 9144 | // Transition back to uninitialized happens only to keep assertions happy |
michael@0 | 9145 | // right before readyState transitions to something else. Make this |
michael@0 | 9146 | // transition undetectable by Web content. |
michael@0 | 9147 | return; |
michael@0 | 9148 | } |
michael@0 | 9149 | if (mTiming) { |
michael@0 | 9150 | switch (rs) { |
michael@0 | 9151 | case READYSTATE_LOADING: |
michael@0 | 9152 | mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI()); |
michael@0 | 9153 | break; |
michael@0 | 9154 | case READYSTATE_INTERACTIVE: |
michael@0 | 9155 | mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI()); |
michael@0 | 9156 | break; |
michael@0 | 9157 | case READYSTATE_COMPLETE: |
michael@0 | 9158 | mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI()); |
michael@0 | 9159 | break; |
michael@0 | 9160 | default: |
michael@0 | 9161 | NS_WARNING("Unexpected ReadyState value"); |
michael@0 | 9162 | break; |
michael@0 | 9163 | } |
michael@0 | 9164 | } |
michael@0 | 9165 | // At the time of loading start, we don't have timing object, record time. |
michael@0 | 9166 | if (READYSTATE_LOADING == rs) { |
michael@0 | 9167 | mLoadingTimeStamp = mozilla::TimeStamp::Now(); |
michael@0 | 9168 | } |
michael@0 | 9169 | |
michael@0 | 9170 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 9171 | new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"), |
michael@0 | 9172 | false, false); |
michael@0 | 9173 | asyncDispatcher->RunDOMEventWhenSafe(); |
michael@0 | 9174 | } |
michael@0 | 9175 | |
michael@0 | 9176 | NS_IMETHODIMP |
michael@0 | 9177 | nsDocument::GetReadyState(nsAString& aReadyState) |
michael@0 | 9178 | { |
michael@0 | 9179 | nsIDocument::GetReadyState(aReadyState); |
michael@0 | 9180 | return NS_OK; |
michael@0 | 9181 | } |
michael@0 | 9182 | |
michael@0 | 9183 | void |
michael@0 | 9184 | nsIDocument::GetReadyState(nsAString& aReadyState) const |
michael@0 | 9185 | { |
michael@0 | 9186 | switch(mReadyState) { |
michael@0 | 9187 | case READYSTATE_LOADING : |
michael@0 | 9188 | aReadyState.Assign(NS_LITERAL_STRING("loading")); |
michael@0 | 9189 | break; |
michael@0 | 9190 | case READYSTATE_INTERACTIVE : |
michael@0 | 9191 | aReadyState.Assign(NS_LITERAL_STRING("interactive")); |
michael@0 | 9192 | break; |
michael@0 | 9193 | case READYSTATE_COMPLETE : |
michael@0 | 9194 | aReadyState.Assign(NS_LITERAL_STRING("complete")); |
michael@0 | 9195 | break; |
michael@0 | 9196 | default: |
michael@0 | 9197 | aReadyState.Assign(NS_LITERAL_STRING("uninitialized")); |
michael@0 | 9198 | } |
michael@0 | 9199 | } |
michael@0 | 9200 | |
michael@0 | 9201 | namespace { |
michael@0 | 9202 | |
michael@0 | 9203 | struct SuppressArgs |
michael@0 | 9204 | { |
michael@0 | 9205 | nsIDocument::SuppressionType mWhat; |
michael@0 | 9206 | uint32_t mIncrease; |
michael@0 | 9207 | }; |
michael@0 | 9208 | |
michael@0 | 9209 | } |
michael@0 | 9210 | |
michael@0 | 9211 | static bool |
michael@0 | 9212 | SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData) |
michael@0 | 9213 | { |
michael@0 | 9214 | SuppressArgs* args = static_cast<SuppressArgs*>(aData); |
michael@0 | 9215 | aDocument->SuppressEventHandling(args->mWhat, args->mIncrease); |
michael@0 | 9216 | return true; |
michael@0 | 9217 | } |
michael@0 | 9218 | |
michael@0 | 9219 | void |
michael@0 | 9220 | nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat, |
michael@0 | 9221 | uint32_t aIncrease) |
michael@0 | 9222 | { |
michael@0 | 9223 | if (mEventsSuppressed == 0 && mAnimationsPaused == 0 && |
michael@0 | 9224 | aIncrease != 0 && mPresShell && mScriptGlobalObject) { |
michael@0 | 9225 | RevokeAnimationFrameNotifications(); |
michael@0 | 9226 | } |
michael@0 | 9227 | |
michael@0 | 9228 | if (aWhat == eAnimationsOnly) { |
michael@0 | 9229 | mAnimationsPaused += aIncrease; |
michael@0 | 9230 | } else { |
michael@0 | 9231 | mEventsSuppressed += aIncrease; |
michael@0 | 9232 | } |
michael@0 | 9233 | |
michael@0 | 9234 | SuppressArgs args = { aWhat, aIncrease }; |
michael@0 | 9235 | EnumerateSubDocuments(SuppressEventHandlingInDocument, &args); |
michael@0 | 9236 | } |
michael@0 | 9237 | |
michael@0 | 9238 | static void |
michael@0 | 9239 | FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments, |
michael@0 | 9240 | bool aFireEvents) |
michael@0 | 9241 | { |
michael@0 | 9242 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 9243 | if (!fm) |
michael@0 | 9244 | return; |
michael@0 | 9245 | |
michael@0 | 9246 | for (uint32_t i = 0; i < aDocuments.Length(); ++i) { |
michael@0 | 9247 | // NB: Don't bother trying to fire delayed events on documents that were |
michael@0 | 9248 | // closed before this event ran. |
michael@0 | 9249 | if (!aDocuments[i]->EventHandlingSuppressed()) { |
michael@0 | 9250 | fm->FireDelayedEvents(aDocuments[i]); |
michael@0 | 9251 | nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell(); |
michael@0 | 9252 | if (shell) { |
michael@0 | 9253 | // Only fire events for active documents. |
michael@0 | 9254 | bool fire = aFireEvents && |
michael@0 | 9255 | aDocuments[i]->GetInnerWindow() && |
michael@0 | 9256 | aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow(); |
michael@0 | 9257 | shell->FireOrClearDelayedEvents(fire); |
michael@0 | 9258 | } |
michael@0 | 9259 | } |
michael@0 | 9260 | } |
michael@0 | 9261 | } |
michael@0 | 9262 | |
michael@0 | 9263 | void |
michael@0 | 9264 | nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr) |
michael@0 | 9265 | { |
michael@0 | 9266 | // Early exit if the img is already present in the img-cache |
michael@0 | 9267 | // which indicates that the "real" load has already started and |
michael@0 | 9268 | // that we shouldn't preload it. |
michael@0 | 9269 | int16_t blockingStatus; |
michael@0 | 9270 | if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) || |
michael@0 | 9271 | !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this), |
michael@0 | 9272 | this, NodePrincipal(), &blockingStatus)) { |
michael@0 | 9273 | return; |
michael@0 | 9274 | } |
michael@0 | 9275 | |
michael@0 | 9276 | nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; |
michael@0 | 9277 | switch (Element::StringToCORSMode(aCrossOriginAttr)) { |
michael@0 | 9278 | case CORS_NONE: |
michael@0 | 9279 | // Nothing to do |
michael@0 | 9280 | break; |
michael@0 | 9281 | case CORS_ANONYMOUS: |
michael@0 | 9282 | loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS; |
michael@0 | 9283 | break; |
michael@0 | 9284 | case CORS_USE_CREDENTIALS: |
michael@0 | 9285 | loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS; |
michael@0 | 9286 | break; |
michael@0 | 9287 | default: |
michael@0 | 9288 | MOZ_CRASH("Unknown CORS mode!"); |
michael@0 | 9289 | } |
michael@0 | 9290 | |
michael@0 | 9291 | // Image not in cache - trigger preload |
michael@0 | 9292 | nsRefPtr<imgRequestProxy> request; |
michael@0 | 9293 | nsresult rv = |
michael@0 | 9294 | nsContentUtils::LoadImage(uri, |
michael@0 | 9295 | this, |
michael@0 | 9296 | NodePrincipal(), |
michael@0 | 9297 | mDocumentURI, // uri of document used as referrer |
michael@0 | 9298 | nullptr, // no observer |
michael@0 | 9299 | loadFlags, |
michael@0 | 9300 | NS_LITERAL_STRING("img"), |
michael@0 | 9301 | getter_AddRefs(request)); |
michael@0 | 9302 | |
michael@0 | 9303 | // Pin image-reference to avoid evicting it from the img-cache before |
michael@0 | 9304 | // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and |
michael@0 | 9305 | // unlink |
michael@0 | 9306 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 9307 | mPreloadingImages.AppendObject(request); |
michael@0 | 9308 | } |
michael@0 | 9309 | } |
michael@0 | 9310 | |
michael@0 | 9311 | EventStates |
michael@0 | 9312 | nsDocument::GetDocumentState() |
michael@0 | 9313 | { |
michael@0 | 9314 | if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) { |
michael@0 | 9315 | if (IsDocumentRightToLeft()) { |
michael@0 | 9316 | mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE; |
michael@0 | 9317 | } |
michael@0 | 9318 | mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE; |
michael@0 | 9319 | } |
michael@0 | 9320 | if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { |
michael@0 | 9321 | nsIPresShell* shell = GetShell(); |
michael@0 | 9322 | if (shell && shell->GetPresContext() && |
michael@0 | 9323 | shell->GetPresContext()->IsTopLevelWindowInactive()) { |
michael@0 | 9324 | mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; |
michael@0 | 9325 | } |
michael@0 | 9326 | mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; |
michael@0 | 9327 | } |
michael@0 | 9328 | return mDocumentState; |
michael@0 | 9329 | } |
michael@0 | 9330 | |
michael@0 | 9331 | namespace { |
michael@0 | 9332 | |
michael@0 | 9333 | /** |
michael@0 | 9334 | * Stub for LoadSheet(), since all we want is to get the sheet into |
michael@0 | 9335 | * the CSSLoader's style cache |
michael@0 | 9336 | */ |
michael@0 | 9337 | class StubCSSLoaderObserver MOZ_FINAL : public nsICSSLoaderObserver { |
michael@0 | 9338 | public: |
michael@0 | 9339 | NS_IMETHOD |
michael@0 | 9340 | StyleSheetLoaded(nsCSSStyleSheet*, bool, nsresult) |
michael@0 | 9341 | { |
michael@0 | 9342 | return NS_OK; |
michael@0 | 9343 | } |
michael@0 | 9344 | NS_DECL_ISUPPORTS |
michael@0 | 9345 | }; |
michael@0 | 9346 | NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver) |
michael@0 | 9347 | |
michael@0 | 9348 | } |
michael@0 | 9349 | |
michael@0 | 9350 | void |
michael@0 | 9351 | nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset, |
michael@0 | 9352 | const nsAString& aCrossOriginAttr) |
michael@0 | 9353 | { |
michael@0 | 9354 | // The CSSLoader will retain this object after we return. |
michael@0 | 9355 | nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver(); |
michael@0 | 9356 | |
michael@0 | 9357 | // Charset names are always ASCII. |
michael@0 | 9358 | CSSLoader()->LoadSheet(uri, NodePrincipal(), |
michael@0 | 9359 | NS_LossyConvertUTF16toASCII(charset), |
michael@0 | 9360 | obs, |
michael@0 | 9361 | Element::StringToCORSMode(aCrossOriginAttr)); |
michael@0 | 9362 | } |
michael@0 | 9363 | |
michael@0 | 9364 | nsresult |
michael@0 | 9365 | nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet, |
michael@0 | 9366 | nsCSSStyleSheet** sheet) |
michael@0 | 9367 | { |
michael@0 | 9368 | return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet); |
michael@0 | 9369 | } |
michael@0 | 9370 | |
michael@0 | 9371 | class nsDelayedEventDispatcher : public nsRunnable |
michael@0 | 9372 | { |
michael@0 | 9373 | public: |
michael@0 | 9374 | nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments) |
michael@0 | 9375 | { |
michael@0 | 9376 | mDocuments.SwapElements(aDocuments); |
michael@0 | 9377 | } |
michael@0 | 9378 | virtual ~nsDelayedEventDispatcher() {} |
michael@0 | 9379 | |
michael@0 | 9380 | NS_IMETHOD Run() |
michael@0 | 9381 | { |
michael@0 | 9382 | FireOrClearDelayedEvents(mDocuments, true); |
michael@0 | 9383 | return NS_OK; |
michael@0 | 9384 | } |
michael@0 | 9385 | |
michael@0 | 9386 | private: |
michael@0 | 9387 | nsTArray<nsCOMPtr<nsIDocument> > mDocuments; |
michael@0 | 9388 | }; |
michael@0 | 9389 | |
michael@0 | 9390 | namespace { |
michael@0 | 9391 | |
michael@0 | 9392 | struct UnsuppressArgs |
michael@0 | 9393 | { |
michael@0 | 9394 | UnsuppressArgs(nsIDocument::SuppressionType aWhat) |
michael@0 | 9395 | : mWhat(aWhat) |
michael@0 | 9396 | { |
michael@0 | 9397 | } |
michael@0 | 9398 | |
michael@0 | 9399 | nsIDocument::SuppressionType mWhat; |
michael@0 | 9400 | nsTArray<nsCOMPtr<nsIDocument>> mDocs; |
michael@0 | 9401 | }; |
michael@0 | 9402 | |
michael@0 | 9403 | } |
michael@0 | 9404 | |
michael@0 | 9405 | static bool |
michael@0 | 9406 | GetAndUnsuppressSubDocuments(nsIDocument* aDocument, |
michael@0 | 9407 | void* aData) |
michael@0 | 9408 | { |
michael@0 | 9409 | UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData); |
michael@0 | 9410 | if (args->mWhat != nsIDocument::eAnimationsOnly && |
michael@0 | 9411 | aDocument->EventHandlingSuppressed() > 0) { |
michael@0 | 9412 | static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression(); |
michael@0 | 9413 | } else if (args->mWhat == nsIDocument::eAnimationsOnly && |
michael@0 | 9414 | aDocument->AnimationsPaused()) { |
michael@0 | 9415 | static_cast<nsDocument*>(aDocument)->ResumeAnimations(); |
michael@0 | 9416 | } |
michael@0 | 9417 | |
michael@0 | 9418 | if (args->mWhat != nsIDocument::eAnimationsOnly) { |
michael@0 | 9419 | // No need to remember documents if we only care about animation frames. |
michael@0 | 9420 | args->mDocs.AppendElement(aDocument); |
michael@0 | 9421 | } |
michael@0 | 9422 | |
michael@0 | 9423 | aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData); |
michael@0 | 9424 | return true; |
michael@0 | 9425 | } |
michael@0 | 9426 | |
michael@0 | 9427 | void |
michael@0 | 9428 | nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat, |
michael@0 | 9429 | bool aFireEvents) |
michael@0 | 9430 | { |
michael@0 | 9431 | UnsuppressArgs args(aWhat); |
michael@0 | 9432 | GetAndUnsuppressSubDocuments(this, &args); |
michael@0 | 9433 | |
michael@0 | 9434 | if (aWhat == nsIDocument::eAnimationsOnly) { |
michael@0 | 9435 | // No need to fire events if we only care about animations here. |
michael@0 | 9436 | return; |
michael@0 | 9437 | } |
michael@0 | 9438 | |
michael@0 | 9439 | if (aFireEvents) { |
michael@0 | 9440 | NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs)); |
michael@0 | 9441 | } else { |
michael@0 | 9442 | FireOrClearDelayedEvents(args.mDocs, false); |
michael@0 | 9443 | } |
michael@0 | 9444 | } |
michael@0 | 9445 | |
michael@0 | 9446 | nsISupports* |
michael@0 | 9447 | nsDocument::GetCurrentContentSink() |
michael@0 | 9448 | { |
michael@0 | 9449 | return mParser ? mParser->GetContentSink() : nullptr; |
michael@0 | 9450 | } |
michael@0 | 9451 | |
michael@0 | 9452 | nsIDocument* |
michael@0 | 9453 | nsDocument::GetTemplateContentsOwner() |
michael@0 | 9454 | { |
michael@0 | 9455 | if (!mTemplateContentsOwner) { |
michael@0 | 9456 | bool hasHadScriptObject = true; |
michael@0 | 9457 | nsIScriptGlobalObject* scriptObject = |
michael@0 | 9458 | GetScriptHandlingObject(hasHadScriptObject); |
michael@0 | 9459 | NS_ENSURE_TRUE(scriptObject || !hasHadScriptObject, nullptr); |
michael@0 | 9460 | |
michael@0 | 9461 | nsCOMPtr<nsIDOMDocument> domDocument; |
michael@0 | 9462 | nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument), |
michael@0 | 9463 | EmptyString(), // aNamespaceURI |
michael@0 | 9464 | EmptyString(), // aQualifiedName |
michael@0 | 9465 | nullptr, // aDoctype |
michael@0 | 9466 | nsIDocument::GetDocumentURI(), |
michael@0 | 9467 | nsIDocument::GetDocBaseURI(), |
michael@0 | 9468 | NodePrincipal(), |
michael@0 | 9469 | true, // aLoadedAsData |
michael@0 | 9470 | scriptObject, // aEventObject |
michael@0 | 9471 | DocumentFlavorHTML); |
michael@0 | 9472 | NS_ENSURE_SUCCESS(rv, nullptr); |
michael@0 | 9473 | |
michael@0 | 9474 | mTemplateContentsOwner = do_QueryInterface(domDocument); |
michael@0 | 9475 | NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr); |
michael@0 | 9476 | |
michael@0 | 9477 | mTemplateContentsOwner->SetScriptHandlingObject(scriptObject); |
michael@0 | 9478 | |
michael@0 | 9479 | // Set |doc| as the template contents owner of itself so that |
michael@0 | 9480 | // |doc| is the template contents owner of template elements created |
michael@0 | 9481 | // by |doc|. |
michael@0 | 9482 | nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get()); |
michael@0 | 9483 | doc->mTemplateContentsOwner = doc; |
michael@0 | 9484 | } |
michael@0 | 9485 | |
michael@0 | 9486 | return mTemplateContentsOwner; |
michael@0 | 9487 | } |
michael@0 | 9488 | |
michael@0 | 9489 | void |
michael@0 | 9490 | nsDocument::RegisterHostObjectUri(const nsACString& aUri) |
michael@0 | 9491 | { |
michael@0 | 9492 | mHostObjectURIs.AppendElement(aUri); |
michael@0 | 9493 | } |
michael@0 | 9494 | |
michael@0 | 9495 | void |
michael@0 | 9496 | nsDocument::UnregisterHostObjectUri(const nsACString& aUri) |
michael@0 | 9497 | { |
michael@0 | 9498 | mHostObjectURIs.RemoveElement(aUri); |
michael@0 | 9499 | } |
michael@0 | 9500 | |
michael@0 | 9501 | void |
michael@0 | 9502 | nsDocument::SetScrollToRef(nsIURI *aDocumentURI) |
michael@0 | 9503 | { |
michael@0 | 9504 | if (!aDocumentURI) { |
michael@0 | 9505 | return; |
michael@0 | 9506 | } |
michael@0 | 9507 | |
michael@0 | 9508 | nsAutoCString ref; |
michael@0 | 9509 | |
michael@0 | 9510 | // Since all URI's that pass through here aren't URL's we can't |
michael@0 | 9511 | // rely on the nsIURI implementation for providing a way for |
michael@0 | 9512 | // finding the 'ref' part of the URI, we'll haveto revert to |
michael@0 | 9513 | // string routines for finding the data past '#' |
michael@0 | 9514 | |
michael@0 | 9515 | aDocumentURI->GetSpec(ref); |
michael@0 | 9516 | |
michael@0 | 9517 | nsReadingIterator<char> start, end; |
michael@0 | 9518 | |
michael@0 | 9519 | ref.BeginReading(start); |
michael@0 | 9520 | ref.EndReading(end); |
michael@0 | 9521 | |
michael@0 | 9522 | if (FindCharInReadable('#', start, end)) { |
michael@0 | 9523 | ++start; // Skip over the '#' |
michael@0 | 9524 | |
michael@0 | 9525 | mScrollToRef = Substring(start, end); |
michael@0 | 9526 | } |
michael@0 | 9527 | } |
michael@0 | 9528 | |
michael@0 | 9529 | void |
michael@0 | 9530 | nsDocument::ScrollToRef() |
michael@0 | 9531 | { |
michael@0 | 9532 | if (mScrolledToRefAlready) { |
michael@0 | 9533 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 9534 | if (shell) { |
michael@0 | 9535 | shell->ScrollToAnchor(); |
michael@0 | 9536 | } |
michael@0 | 9537 | return; |
michael@0 | 9538 | } |
michael@0 | 9539 | |
michael@0 | 9540 | if (mScrollToRef.IsEmpty()) { |
michael@0 | 9541 | return; |
michael@0 | 9542 | } |
michael@0 | 9543 | |
michael@0 | 9544 | char* tmpstr = ToNewCString(mScrollToRef); |
michael@0 | 9545 | if (!tmpstr) { |
michael@0 | 9546 | return; |
michael@0 | 9547 | } |
michael@0 | 9548 | |
michael@0 | 9549 | nsUnescape(tmpstr); |
michael@0 | 9550 | nsAutoCString unescapedRef; |
michael@0 | 9551 | unescapedRef.Assign(tmpstr); |
michael@0 | 9552 | nsMemory::Free(tmpstr); |
michael@0 | 9553 | |
michael@0 | 9554 | nsresult rv = NS_ERROR_FAILURE; |
michael@0 | 9555 | // We assume that the bytes are in UTF-8, as it says in the spec: |
michael@0 | 9556 | // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1 |
michael@0 | 9557 | NS_ConvertUTF8toUTF16 ref(unescapedRef); |
michael@0 | 9558 | |
michael@0 | 9559 | nsCOMPtr<nsIPresShell> shell = GetShell(); |
michael@0 | 9560 | if (shell) { |
michael@0 | 9561 | // Check an empty string which might be caused by the UTF-8 conversion |
michael@0 | 9562 | if (!ref.IsEmpty()) { |
michael@0 | 9563 | // Note that GoToAnchor will handle flushing layout as needed. |
michael@0 | 9564 | rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef); |
michael@0 | 9565 | } else { |
michael@0 | 9566 | rv = NS_ERROR_FAILURE; |
michael@0 | 9567 | } |
michael@0 | 9568 | |
michael@0 | 9569 | // If UTF-8 URI failed then try to assume the string as a |
michael@0 | 9570 | // document's charset. |
michael@0 | 9571 | |
michael@0 | 9572 | if (NS_FAILED(rv)) { |
michael@0 | 9573 | const nsACString &docCharset = GetDocumentCharacterSet(); |
michael@0 | 9574 | |
michael@0 | 9575 | rv = nsContentUtils::ConvertStringFromEncoding(docCharset, |
michael@0 | 9576 | unescapedRef, |
michael@0 | 9577 | ref); |
michael@0 | 9578 | |
michael@0 | 9579 | if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) { |
michael@0 | 9580 | rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef); |
michael@0 | 9581 | } |
michael@0 | 9582 | } |
michael@0 | 9583 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 9584 | mScrolledToRefAlready = true; |
michael@0 | 9585 | } |
michael@0 | 9586 | } |
michael@0 | 9587 | } |
michael@0 | 9588 | |
michael@0 | 9589 | void |
michael@0 | 9590 | nsDocument::ResetScrolledToRefAlready() |
michael@0 | 9591 | { |
michael@0 | 9592 | mScrolledToRefAlready = false; |
michael@0 | 9593 | } |
michael@0 | 9594 | |
michael@0 | 9595 | void |
michael@0 | 9596 | nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue) |
michael@0 | 9597 | { |
michael@0 | 9598 | mChangeScrollPosWhenScrollingToRef = aValue; |
michael@0 | 9599 | } |
michael@0 | 9600 | |
michael@0 | 9601 | void |
michael@0 | 9602 | nsIDocument::RegisterFreezableElement(nsIContent* aContent) |
michael@0 | 9603 | { |
michael@0 | 9604 | if (!mFreezableElements) { |
michael@0 | 9605 | mFreezableElements = new nsTHashtable<nsPtrHashKey<nsIContent> >(); |
michael@0 | 9606 | if (!mFreezableElements) |
michael@0 | 9607 | return; |
michael@0 | 9608 | } |
michael@0 | 9609 | mFreezableElements->PutEntry(aContent); |
michael@0 | 9610 | } |
michael@0 | 9611 | |
michael@0 | 9612 | bool |
michael@0 | 9613 | nsIDocument::UnregisterFreezableElement(nsIContent* aContent) |
michael@0 | 9614 | { |
michael@0 | 9615 | if (!mFreezableElements) |
michael@0 | 9616 | return false; |
michael@0 | 9617 | if (!mFreezableElements->GetEntry(aContent)) |
michael@0 | 9618 | return false; |
michael@0 | 9619 | mFreezableElements->RemoveEntry(aContent); |
michael@0 | 9620 | return true; |
michael@0 | 9621 | } |
michael@0 | 9622 | |
michael@0 | 9623 | struct EnumerateFreezablesData { |
michael@0 | 9624 | nsIDocument::FreezableElementEnumerator mEnumerator; |
michael@0 | 9625 | void* mData; |
michael@0 | 9626 | }; |
michael@0 | 9627 | |
michael@0 | 9628 | static PLDHashOperator |
michael@0 | 9629 | EnumerateFreezables(nsPtrHashKey<nsIContent>* aEntry, void* aData) |
michael@0 | 9630 | { |
michael@0 | 9631 | EnumerateFreezablesData* data = static_cast<EnumerateFreezablesData*>(aData); |
michael@0 | 9632 | data->mEnumerator(aEntry->GetKey(), data->mData); |
michael@0 | 9633 | return PL_DHASH_NEXT; |
michael@0 | 9634 | } |
michael@0 | 9635 | |
michael@0 | 9636 | void |
michael@0 | 9637 | nsIDocument::EnumerateFreezableElements(FreezableElementEnumerator aEnumerator, |
michael@0 | 9638 | void* aData) |
michael@0 | 9639 | { |
michael@0 | 9640 | if (!mFreezableElements) |
michael@0 | 9641 | return; |
michael@0 | 9642 | EnumerateFreezablesData data = { aEnumerator, aData }; |
michael@0 | 9643 | mFreezableElements->EnumerateEntries(EnumerateFreezables, &data); |
michael@0 | 9644 | } |
michael@0 | 9645 | |
michael@0 | 9646 | void |
michael@0 | 9647 | nsIDocument::RegisterPendingLinkUpdate(Link* aLink) |
michael@0 | 9648 | { |
michael@0 | 9649 | MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden); |
michael@0 | 9650 | mLinksToUpdate.PutEntry(aLink); |
michael@0 | 9651 | mHasLinksToUpdate = true; |
michael@0 | 9652 | } |
michael@0 | 9653 | |
michael@0 | 9654 | void |
michael@0 | 9655 | nsIDocument::UnregisterPendingLinkUpdate(Link* aLink) |
michael@0 | 9656 | { |
michael@0 | 9657 | MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden); |
michael@0 | 9658 | if (!mHasLinksToUpdate) |
michael@0 | 9659 | return; |
michael@0 | 9660 | |
michael@0 | 9661 | mLinksToUpdate.RemoveEntry(aLink); |
michael@0 | 9662 | } |
michael@0 | 9663 | |
michael@0 | 9664 | static PLDHashOperator |
michael@0 | 9665 | EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData) |
michael@0 | 9666 | { |
michael@0 | 9667 | aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState()); |
michael@0 | 9668 | return PL_DHASH_NEXT; |
michael@0 | 9669 | } |
michael@0 | 9670 | |
michael@0 | 9671 | void |
michael@0 | 9672 | nsIDocument::FlushPendingLinkUpdates() |
michael@0 | 9673 | { |
michael@0 | 9674 | MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden); |
michael@0 | 9675 | if (!mHasLinksToUpdate) |
michael@0 | 9676 | return; |
michael@0 | 9677 | |
michael@0 | 9678 | #ifdef DEBUG |
michael@0 | 9679 | AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden); |
michael@0 | 9680 | mIsLinkUpdateRegistrationsForbidden = true; |
michael@0 | 9681 | #endif |
michael@0 | 9682 | mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nullptr); |
michael@0 | 9683 | mLinksToUpdate.Clear(); |
michael@0 | 9684 | mHasLinksToUpdate = false; |
michael@0 | 9685 | } |
michael@0 | 9686 | |
michael@0 | 9687 | already_AddRefed<nsIDocument> |
michael@0 | 9688 | nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer) |
michael@0 | 9689 | { |
michael@0 | 9690 | nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(this); |
michael@0 | 9691 | NS_ENSURE_TRUE(domDoc, nullptr); |
michael@0 | 9692 | mCreatingStaticClone = true; |
michael@0 | 9693 | |
michael@0 | 9694 | // Make document use different container during cloning. |
michael@0 | 9695 | nsRefPtr<nsDocShell> originalShell = mDocumentContainer.get(); |
michael@0 | 9696 | SetContainer(static_cast<nsDocShell*>(aCloneContainer)); |
michael@0 | 9697 | nsCOMPtr<nsIDOMNode> clonedNode; |
michael@0 | 9698 | nsresult rv = domDoc->CloneNode(true, 1, getter_AddRefs(clonedNode)); |
michael@0 | 9699 | SetContainer(originalShell); |
michael@0 | 9700 | |
michael@0 | 9701 | nsCOMPtr<nsIDocument> clonedDoc; |
michael@0 | 9702 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 9703 | clonedDoc = do_QueryInterface(clonedNode); |
michael@0 | 9704 | if (clonedDoc) { |
michael@0 | 9705 | if (IsStaticDocument()) { |
michael@0 | 9706 | clonedDoc->mOriginalDocument = mOriginalDocument; |
michael@0 | 9707 | } else { |
michael@0 | 9708 | clonedDoc->mOriginalDocument = this; |
michael@0 | 9709 | } |
michael@0 | 9710 | int32_t sheetsCount = GetNumberOfStyleSheets(); |
michael@0 | 9711 | for (int32_t i = 0; i < sheetsCount; ++i) { |
michael@0 | 9712 | nsRefPtr<nsCSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i)); |
michael@0 | 9713 | if (sheet) { |
michael@0 | 9714 | if (sheet->IsApplicable()) { |
michael@0 | 9715 | nsRefPtr<nsCSSStyleSheet> clonedSheet = |
michael@0 | 9716 | sheet->Clone(nullptr, nullptr, clonedDoc, nullptr); |
michael@0 | 9717 | NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!"); |
michael@0 | 9718 | if (clonedSheet) { |
michael@0 | 9719 | clonedDoc->AddStyleSheet(clonedSheet); |
michael@0 | 9720 | } |
michael@0 | 9721 | } |
michael@0 | 9722 | } |
michael@0 | 9723 | } |
michael@0 | 9724 | |
michael@0 | 9725 | sheetsCount = GetNumberOfCatalogStyleSheets(); |
michael@0 | 9726 | for (int32_t i = 0; i < sheetsCount; ++i) { |
michael@0 | 9727 | nsRefPtr<nsCSSStyleSheet> sheet = |
michael@0 | 9728 | do_QueryObject(GetCatalogStyleSheetAt(i)); |
michael@0 | 9729 | if (sheet) { |
michael@0 | 9730 | if (sheet->IsApplicable()) { |
michael@0 | 9731 | nsRefPtr<nsCSSStyleSheet> clonedSheet = |
michael@0 | 9732 | sheet->Clone(nullptr, nullptr, clonedDoc, nullptr); |
michael@0 | 9733 | NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!"); |
michael@0 | 9734 | if (clonedSheet) { |
michael@0 | 9735 | clonedDoc->AddCatalogStyleSheet(clonedSheet); |
michael@0 | 9736 | } |
michael@0 | 9737 | } |
michael@0 | 9738 | } |
michael@0 | 9739 | } |
michael@0 | 9740 | } |
michael@0 | 9741 | } |
michael@0 | 9742 | mCreatingStaticClone = false; |
michael@0 | 9743 | return clonedDoc.forget(); |
michael@0 | 9744 | } |
michael@0 | 9745 | |
michael@0 | 9746 | nsresult |
michael@0 | 9747 | nsIDocument::ScheduleFrameRequestCallback(const FrameRequestCallbackHolder& aCallback, |
michael@0 | 9748 | int32_t *aHandle) |
michael@0 | 9749 | { |
michael@0 | 9750 | if (mFrameRequestCallbackCounter == INT32_MAX) { |
michael@0 | 9751 | // Can't increment without overflowing; bail out |
michael@0 | 9752 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 9753 | } |
michael@0 | 9754 | int32_t newHandle = ++mFrameRequestCallbackCounter; |
michael@0 | 9755 | |
michael@0 | 9756 | bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty(); |
michael@0 | 9757 | DebugOnly<FrameRequest*> request = |
michael@0 | 9758 | mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle)); |
michael@0 | 9759 | NS_ASSERTION(request, "This is supposed to be infallible!"); |
michael@0 | 9760 | if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) { |
michael@0 | 9761 | mPresShell->GetPresContext()->RefreshDriver()-> |
michael@0 | 9762 | ScheduleFrameRequestCallbacks(this); |
michael@0 | 9763 | } |
michael@0 | 9764 | |
michael@0 | 9765 | *aHandle = newHandle; |
michael@0 | 9766 | return NS_OK; |
michael@0 | 9767 | } |
michael@0 | 9768 | |
michael@0 | 9769 | void |
michael@0 | 9770 | nsIDocument::CancelFrameRequestCallback(int32_t aHandle) |
michael@0 | 9771 | { |
michael@0 | 9772 | // mFrameRequestCallbacks is stored sorted by handle |
michael@0 | 9773 | if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) && |
michael@0 | 9774 | mFrameRequestCallbacks.IsEmpty() && |
michael@0 | 9775 | mPresShell && IsEventHandlingEnabled()) { |
michael@0 | 9776 | mPresShell->GetPresContext()->RefreshDriver()-> |
michael@0 | 9777 | RevokeFrameRequestCallbacks(this); |
michael@0 | 9778 | } |
michael@0 | 9779 | } |
michael@0 | 9780 | |
michael@0 | 9781 | nsresult |
michael@0 | 9782 | nsDocument::GetStateObject(nsIVariant** aState) |
michael@0 | 9783 | { |
michael@0 | 9784 | // Get the document's current state object. This is the object backing both |
michael@0 | 9785 | // history.state and popStateEvent.state. |
michael@0 | 9786 | // |
michael@0 | 9787 | // mStateObjectContainer may be null; this just means that there's no |
michael@0 | 9788 | // current state object. |
michael@0 | 9789 | |
michael@0 | 9790 | nsCOMPtr<nsIVariant> stateObj; |
michael@0 | 9791 | if (!mStateObjectCached && mStateObjectContainer) { |
michael@0 | 9792 | AutoJSContext cx; |
michael@0 | 9793 | nsIGlobalObject* sgo = GetScopeObject(); |
michael@0 | 9794 | NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); |
michael@0 | 9795 | JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); |
michael@0 | 9796 | NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED); |
michael@0 | 9797 | JSAutoCompartment ac(cx, global); |
michael@0 | 9798 | |
michael@0 | 9799 | mStateObjectContainer-> |
michael@0 | 9800 | DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached)); |
michael@0 | 9801 | } |
michael@0 | 9802 | |
michael@0 | 9803 | NS_IF_ADDREF(*aState = mStateObjectCached); |
michael@0 | 9804 | |
michael@0 | 9805 | return NS_OK; |
michael@0 | 9806 | } |
michael@0 | 9807 | |
michael@0 | 9808 | nsDOMNavigationTiming* |
michael@0 | 9809 | nsDocument::GetNavigationTiming() const |
michael@0 | 9810 | { |
michael@0 | 9811 | return mTiming; |
michael@0 | 9812 | } |
michael@0 | 9813 | |
michael@0 | 9814 | nsresult |
michael@0 | 9815 | nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming) |
michael@0 | 9816 | { |
michael@0 | 9817 | mTiming = aTiming; |
michael@0 | 9818 | if (!mLoadingTimeStamp.IsNull() && mTiming) { |
michael@0 | 9819 | mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp); |
michael@0 | 9820 | } |
michael@0 | 9821 | return NS_OK; |
michael@0 | 9822 | } |
michael@0 | 9823 | |
michael@0 | 9824 | Element* |
michael@0 | 9825 | nsDocument::FindImageMap(const nsAString& aUseMapValue) |
michael@0 | 9826 | { |
michael@0 | 9827 | if (aUseMapValue.IsEmpty()) { |
michael@0 | 9828 | return nullptr; |
michael@0 | 9829 | } |
michael@0 | 9830 | |
michael@0 | 9831 | nsAString::const_iterator start, end; |
michael@0 | 9832 | aUseMapValue.BeginReading(start); |
michael@0 | 9833 | aUseMapValue.EndReading(end); |
michael@0 | 9834 | |
michael@0 | 9835 | int32_t hash = aUseMapValue.FindChar('#'); |
michael@0 | 9836 | if (hash < 0) { |
michael@0 | 9837 | return nullptr; |
michael@0 | 9838 | } |
michael@0 | 9839 | // aUsemap contains a '#', set start to point right after the '#' |
michael@0 | 9840 | start.advance(hash + 1); |
michael@0 | 9841 | |
michael@0 | 9842 | if (start == end) { |
michael@0 | 9843 | return nullptr; // aUsemap == "#" |
michael@0 | 9844 | } |
michael@0 | 9845 | |
michael@0 | 9846 | const nsAString& mapName = Substring(start, end); |
michael@0 | 9847 | |
michael@0 | 9848 | if (!mImageMaps) { |
michael@0 | 9849 | mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map); |
michael@0 | 9850 | } |
michael@0 | 9851 | |
michael@0 | 9852 | uint32_t i, n = mImageMaps->Length(true); |
michael@0 | 9853 | nsString name; |
michael@0 | 9854 | for (i = 0; i < n; ++i) { |
michael@0 | 9855 | nsIContent* map = mImageMaps->Item(i); |
michael@0 | 9856 | if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName, |
michael@0 | 9857 | eCaseMatters) || |
michael@0 | 9858 | (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) && |
michael@0 | 9859 | mapName.Equals(name, nsCaseInsensitiveStringComparator()))) { |
michael@0 | 9860 | return map->AsElement(); |
michael@0 | 9861 | } |
michael@0 | 9862 | } |
michael@0 | 9863 | |
michael@0 | 9864 | return nullptr; |
michael@0 | 9865 | } |
michael@0 | 9866 | |
michael@0 | 9867 | #define DEPRECATED_OPERATION(_op) #_op "Warning", |
michael@0 | 9868 | static const char* kWarnings[] = { |
michael@0 | 9869 | #include "nsDeprecatedOperationList.h" |
michael@0 | 9870 | nullptr |
michael@0 | 9871 | }; |
michael@0 | 9872 | #undef DEPRECATED_OPERATION |
michael@0 | 9873 | |
michael@0 | 9874 | void |
michael@0 | 9875 | nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation, |
michael@0 | 9876 | bool asError /* = false */) |
michael@0 | 9877 | { |
michael@0 | 9878 | static_assert(eDeprecatedOperationCount <= 64, |
michael@0 | 9879 | "Too many deprecated operations"); |
michael@0 | 9880 | if (mWarnedAbout & (1ull << aOperation)) { |
michael@0 | 9881 | return; |
michael@0 | 9882 | } |
michael@0 | 9883 | mWarnedAbout |= (1ull << aOperation); |
michael@0 | 9884 | uint32_t flags = asError ? nsIScriptError::errorFlag |
michael@0 | 9885 | : nsIScriptError::warningFlag; |
michael@0 | 9886 | nsContentUtils::ReportToConsole(flags, |
michael@0 | 9887 | NS_LITERAL_CSTRING("DOM Core"), this, |
michael@0 | 9888 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 9889 | kWarnings[aOperation]); |
michael@0 | 9890 | } |
michael@0 | 9891 | |
michael@0 | 9892 | nsresult |
michael@0 | 9893 | nsDocument::AddImage(imgIRequest* aImage) |
michael@0 | 9894 | { |
michael@0 | 9895 | NS_ENSURE_ARG_POINTER(aImage); |
michael@0 | 9896 | |
michael@0 | 9897 | // See if the image is already in the hashtable. If it is, get the old count. |
michael@0 | 9898 | uint32_t oldCount = 0; |
michael@0 | 9899 | mImageTracker.Get(aImage, &oldCount); |
michael@0 | 9900 | |
michael@0 | 9901 | // Put the image in the hashtable, with the proper count. |
michael@0 | 9902 | mImageTracker.Put(aImage, oldCount + 1); |
michael@0 | 9903 | |
michael@0 | 9904 | nsresult rv = NS_OK; |
michael@0 | 9905 | |
michael@0 | 9906 | // If this is the first insertion and we're locking images, lock this image |
michael@0 | 9907 | // too. |
michael@0 | 9908 | if (oldCount == 0) { |
michael@0 | 9909 | if (mLockingImages) |
michael@0 | 9910 | rv = aImage->LockImage(); |
michael@0 | 9911 | if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit || |
michael@0 | 9912 | mImageTracker.Count() < sOnloadDecodeLimit)) |
michael@0 | 9913 | rv = aImage->StartDecoding(); |
michael@0 | 9914 | } |
michael@0 | 9915 | |
michael@0 | 9916 | // If this is the first insertion and we're animating images, request |
michael@0 | 9917 | // that this image be animated too. |
michael@0 | 9918 | if (oldCount == 0 && mAnimatingImages) { |
michael@0 | 9919 | nsresult rv2 = aImage->IncrementAnimationConsumers(); |
michael@0 | 9920 | rv = NS_SUCCEEDED(rv) ? rv2 : rv; |
michael@0 | 9921 | } |
michael@0 | 9922 | |
michael@0 | 9923 | return rv; |
michael@0 | 9924 | } |
michael@0 | 9925 | |
michael@0 | 9926 | nsresult |
michael@0 | 9927 | nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags) |
michael@0 | 9928 | { |
michael@0 | 9929 | NS_ENSURE_ARG_POINTER(aImage); |
michael@0 | 9930 | |
michael@0 | 9931 | // Get the old count. It should exist and be > 0. |
michael@0 | 9932 | uint32_t count = 0; |
michael@0 | 9933 | DebugOnly<bool> found = mImageTracker.Get(aImage, &count); |
michael@0 | 9934 | NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!"); |
michael@0 | 9935 | NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!"); |
michael@0 | 9936 | |
michael@0 | 9937 | // We're removing, so decrement the count. |
michael@0 | 9938 | count--; |
michael@0 | 9939 | |
michael@0 | 9940 | // If the count is now zero, remove from the tracker. |
michael@0 | 9941 | // Otherwise, set the new value. |
michael@0 | 9942 | if (count != 0) { |
michael@0 | 9943 | mImageTracker.Put(aImage, count); |
michael@0 | 9944 | return NS_OK; |
michael@0 | 9945 | } |
michael@0 | 9946 | |
michael@0 | 9947 | mImageTracker.Remove(aImage); |
michael@0 | 9948 | |
michael@0 | 9949 | nsresult rv = NS_OK; |
michael@0 | 9950 | |
michael@0 | 9951 | // Now that we're no longer tracking this image, unlock it if we'd |
michael@0 | 9952 | // previously locked it. |
michael@0 | 9953 | if (mLockingImages) { |
michael@0 | 9954 | rv = aImage->UnlockImage(); |
michael@0 | 9955 | } |
michael@0 | 9956 | |
michael@0 | 9957 | // If we're animating images, remove our request to animate this one. |
michael@0 | 9958 | if (mAnimatingImages) { |
michael@0 | 9959 | nsresult rv2 = aImage->DecrementAnimationConsumers(); |
michael@0 | 9960 | rv = NS_SUCCEEDED(rv) ? rv2 : rv; |
michael@0 | 9961 | } |
michael@0 | 9962 | |
michael@0 | 9963 | if (aFlags & REQUEST_DISCARD) { |
michael@0 | 9964 | // Request that the image be discarded if nobody else holds a lock on it. |
michael@0 | 9965 | // Do this even if !mLockingImages, because even if we didn't just unlock |
michael@0 | 9966 | // this image, it might still be a candidate for discarding. |
michael@0 | 9967 | aImage->RequestDiscard(); |
michael@0 | 9968 | } |
michael@0 | 9969 | |
michael@0 | 9970 | return rv; |
michael@0 | 9971 | } |
michael@0 | 9972 | |
michael@0 | 9973 | nsresult |
michael@0 | 9974 | nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin) |
michael@0 | 9975 | { |
michael@0 | 9976 | MOZ_ASSERT(aPlugin); |
michael@0 | 9977 | if (!mPlugins.PutEntry(aPlugin)) { |
michael@0 | 9978 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 9979 | } |
michael@0 | 9980 | return NS_OK; |
michael@0 | 9981 | } |
michael@0 | 9982 | |
michael@0 | 9983 | void |
michael@0 | 9984 | nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin) |
michael@0 | 9985 | { |
michael@0 | 9986 | MOZ_ASSERT(aPlugin); |
michael@0 | 9987 | mPlugins.RemoveEntry(aPlugin); |
michael@0 | 9988 | } |
michael@0 | 9989 | |
michael@0 | 9990 | static bool |
michael@0 | 9991 | AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg) |
michael@0 | 9992 | { |
michael@0 | 9993 | nsTArray<nsIObjectLoadingContent*>* plugins = |
michael@0 | 9994 | reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg); |
michael@0 | 9995 | MOZ_ASSERT(plugins); |
michael@0 | 9996 | aDocument->GetPlugins(*plugins); |
michael@0 | 9997 | return true; |
michael@0 | 9998 | } |
michael@0 | 9999 | |
michael@0 | 10000 | static PLDHashOperator |
michael@0 | 10001 | AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg) |
michael@0 | 10002 | { |
michael@0 | 10003 | nsTArray<nsIObjectLoadingContent*>* allPlugins = |
michael@0 | 10004 | reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg); |
michael@0 | 10005 | MOZ_ASSERT(allPlugins); |
michael@0 | 10006 | allPlugins->AppendElement(aPlugin->GetKey()); |
michael@0 | 10007 | return PL_DHASH_NEXT; |
michael@0 | 10008 | } |
michael@0 | 10009 | |
michael@0 | 10010 | void |
michael@0 | 10011 | nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) |
michael@0 | 10012 | { |
michael@0 | 10013 | aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count()); |
michael@0 | 10014 | mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins); |
michael@0 | 10015 | EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins); |
michael@0 | 10016 | } |
michael@0 | 10017 | |
michael@0 | 10018 | PLDHashOperator LockEnumerator(imgIRequest* aKey, |
michael@0 | 10019 | uint32_t aData, |
michael@0 | 10020 | void* userArg) |
michael@0 | 10021 | { |
michael@0 | 10022 | aKey->LockImage(); |
michael@0 | 10023 | aKey->RequestDecode(); |
michael@0 | 10024 | return PL_DHASH_NEXT; |
michael@0 | 10025 | } |
michael@0 | 10026 | |
michael@0 | 10027 | PLDHashOperator UnlockEnumerator(imgIRequest* aKey, |
michael@0 | 10028 | uint32_t aData, |
michael@0 | 10029 | void* userArg) |
michael@0 | 10030 | { |
michael@0 | 10031 | aKey->UnlockImage(); |
michael@0 | 10032 | return PL_DHASH_NEXT; |
michael@0 | 10033 | } |
michael@0 | 10034 | |
michael@0 | 10035 | |
michael@0 | 10036 | nsresult |
michael@0 | 10037 | nsDocument::SetImageLockingState(bool aLocked) |
michael@0 | 10038 | { |
michael@0 | 10039 | if (XRE_GetProcessType() == GeckoProcessType_Content && |
michael@0 | 10040 | !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) { |
michael@0 | 10041 | return NS_OK; |
michael@0 | 10042 | } |
michael@0 | 10043 | |
michael@0 | 10044 | // If there's no change, there's nothing to do. |
michael@0 | 10045 | if (mLockingImages == aLocked) |
michael@0 | 10046 | return NS_OK; |
michael@0 | 10047 | |
michael@0 | 10048 | // Otherwise, iterate over our images and perform the appropriate action. |
michael@0 | 10049 | mImageTracker.EnumerateRead(aLocked ? LockEnumerator |
michael@0 | 10050 | : UnlockEnumerator, |
michael@0 | 10051 | nullptr); |
michael@0 | 10052 | |
michael@0 | 10053 | // Update state. |
michael@0 | 10054 | mLockingImages = aLocked; |
michael@0 | 10055 | |
michael@0 | 10056 | return NS_OK; |
michael@0 | 10057 | } |
michael@0 | 10058 | |
michael@0 | 10059 | PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey, |
michael@0 | 10060 | uint32_t aData, |
michael@0 | 10061 | void* userArg) |
michael@0 | 10062 | { |
michael@0 | 10063 | aKey->IncrementAnimationConsumers(); |
michael@0 | 10064 | return PL_DHASH_NEXT; |
michael@0 | 10065 | } |
michael@0 | 10066 | |
michael@0 | 10067 | PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey, |
michael@0 | 10068 | uint32_t aData, |
michael@0 | 10069 | void* userArg) |
michael@0 | 10070 | { |
michael@0 | 10071 | aKey->DecrementAnimationConsumers(); |
michael@0 | 10072 | return PL_DHASH_NEXT; |
michael@0 | 10073 | } |
michael@0 | 10074 | |
michael@0 | 10075 | void |
michael@0 | 10076 | nsDocument::SetImagesNeedAnimating(bool aAnimating) |
michael@0 | 10077 | { |
michael@0 | 10078 | // If there's no change, there's nothing to do. |
michael@0 | 10079 | if (mAnimatingImages == aAnimating) |
michael@0 | 10080 | return; |
michael@0 | 10081 | |
michael@0 | 10082 | // Otherwise, iterate over our images and perform the appropriate action. |
michael@0 | 10083 | mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator |
michael@0 | 10084 | : DecrementAnimationEnumerator, |
michael@0 | 10085 | nullptr); |
michael@0 | 10086 | |
michael@0 | 10087 | // Update state. |
michael@0 | 10088 | mAnimatingImages = aAnimating; |
michael@0 | 10089 | } |
michael@0 | 10090 | |
michael@0 | 10091 | already_AddRefed<Touch> |
michael@0 | 10092 | nsIDocument::CreateTouch(nsIDOMWindow* aView, |
michael@0 | 10093 | EventTarget* aTarget, |
michael@0 | 10094 | int32_t aIdentifier, |
michael@0 | 10095 | int32_t aPageX, int32_t aPageY, |
michael@0 | 10096 | int32_t aScreenX, int32_t aScreenY, |
michael@0 | 10097 | int32_t aClientX, int32_t aClientY, |
michael@0 | 10098 | int32_t aRadiusX, int32_t aRadiusY, |
michael@0 | 10099 | float aRotationAngle, |
michael@0 | 10100 | float aForce) |
michael@0 | 10101 | { |
michael@0 | 10102 | nsRefPtr<Touch> touch = new Touch(aTarget, |
michael@0 | 10103 | aIdentifier, |
michael@0 | 10104 | aPageX, aPageY, |
michael@0 | 10105 | aScreenX, aScreenY, |
michael@0 | 10106 | aClientX, aClientY, |
michael@0 | 10107 | aRadiusX, aRadiusY, |
michael@0 | 10108 | aRotationAngle, |
michael@0 | 10109 | aForce); |
michael@0 | 10110 | return touch.forget(); |
michael@0 | 10111 | } |
michael@0 | 10112 | |
michael@0 | 10113 | already_AddRefed<TouchList> |
michael@0 | 10114 | nsIDocument::CreateTouchList() |
michael@0 | 10115 | { |
michael@0 | 10116 | nsRefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
michael@0 | 10117 | return retval.forget(); |
michael@0 | 10118 | } |
michael@0 | 10119 | |
michael@0 | 10120 | already_AddRefed<TouchList> |
michael@0 | 10121 | nsIDocument::CreateTouchList(Touch& aTouch, |
michael@0 | 10122 | const Sequence<OwningNonNull<Touch> >& aTouches) |
michael@0 | 10123 | { |
michael@0 | 10124 | nsRefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
michael@0 | 10125 | retval->Append(&aTouch); |
michael@0 | 10126 | for (uint32_t i = 0; i < aTouches.Length(); ++i) { |
michael@0 | 10127 | retval->Append(aTouches[i].get()); |
michael@0 | 10128 | } |
michael@0 | 10129 | return retval.forget(); |
michael@0 | 10130 | } |
michael@0 | 10131 | |
michael@0 | 10132 | already_AddRefed<TouchList> |
michael@0 | 10133 | nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches) |
michael@0 | 10134 | { |
michael@0 | 10135 | nsRefPtr<TouchList> retval = new TouchList(ToSupports(this)); |
michael@0 | 10136 | for (uint32_t i = 0; i < aTouches.Length(); ++i) { |
michael@0 | 10137 | retval->Append(aTouches[i].get()); |
michael@0 | 10138 | } |
michael@0 | 10139 | return retval.forget(); |
michael@0 | 10140 | } |
michael@0 | 10141 | |
michael@0 | 10142 | already_AddRefed<nsDOMCaretPosition> |
michael@0 | 10143 | nsIDocument::CaretPositionFromPoint(float aX, float aY) |
michael@0 | 10144 | { |
michael@0 | 10145 | nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); |
michael@0 | 10146 | nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); |
michael@0 | 10147 | nsPoint pt(x, y); |
michael@0 | 10148 | |
michael@0 | 10149 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 10150 | |
michael@0 | 10151 | nsIPresShell *ps = GetShell(); |
michael@0 | 10152 | if (!ps) { |
michael@0 | 10153 | return nullptr; |
michael@0 | 10154 | } |
michael@0 | 10155 | |
michael@0 | 10156 | nsIFrame *rootFrame = ps->GetRootFrame(); |
michael@0 | 10157 | |
michael@0 | 10158 | // XUL docs, unlike HTML, have no frame tree until everything's done loading |
michael@0 | 10159 | if (!rootFrame) { |
michael@0 | 10160 | return nullptr; |
michael@0 | 10161 | } |
michael@0 | 10162 | |
michael@0 | 10163 | nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, |
michael@0 | 10164 | nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC); |
michael@0 | 10165 | if (!ptFrame) { |
michael@0 | 10166 | return nullptr; |
michael@0 | 10167 | } |
michael@0 | 10168 | |
michael@0 | 10169 | // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need |
michael@0 | 10170 | // to adjust to frame-relative coordinates before we can perform this call. |
michael@0 | 10171 | // It should also not take into account the padding of the frame. |
michael@0 | 10172 | nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame); |
michael@0 | 10173 | |
michael@0 | 10174 | nsFrame::ContentOffsets offsets = |
michael@0 | 10175 | ptFrame->GetContentOffsetsFromPoint(adjustedPoint); |
michael@0 | 10176 | |
michael@0 | 10177 | nsCOMPtr<nsIContent> node = offsets.content; |
michael@0 | 10178 | uint32_t offset = offsets.offset; |
michael@0 | 10179 | nsCOMPtr<nsIContent> anonNode = node; |
michael@0 | 10180 | bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree(); |
michael@0 | 10181 | if (nodeIsAnonymous) { |
michael@0 | 10182 | node = ptFrame->GetContent(); |
michael@0 | 10183 | nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent(); |
michael@0 | 10184 | nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon); |
michael@0 | 10185 | nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon); |
michael@0 | 10186 | bool isText; |
michael@0 | 10187 | if (textArea || (input && |
michael@0 | 10188 | NS_SUCCEEDED(input->MozIsTextField(false, &isText)) && |
michael@0 | 10189 | isText)) { |
michael@0 | 10190 | // If the anonymous content node has a child, then we need to make sure |
michael@0 | 10191 | // that we get the appropriate child, as otherwise the offset may not be |
michael@0 | 10192 | // correct when we construct a range for it. |
michael@0 | 10193 | nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild(); |
michael@0 | 10194 | if (firstChild) { |
michael@0 | 10195 | anonNode = firstChild; |
michael@0 | 10196 | } |
michael@0 | 10197 | |
michael@0 | 10198 | if (textArea) { |
michael@0 | 10199 | offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset); |
michael@0 | 10200 | } |
michael@0 | 10201 | |
michael@0 | 10202 | node = nonanon; |
michael@0 | 10203 | } else { |
michael@0 | 10204 | node = nullptr; |
michael@0 | 10205 | offset = 0; |
michael@0 | 10206 | } |
michael@0 | 10207 | } |
michael@0 | 10208 | |
michael@0 | 10209 | nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset); |
michael@0 | 10210 | if (nodeIsAnonymous) { |
michael@0 | 10211 | aCaretPos->SetAnonymousContentNode(anonNode); |
michael@0 | 10212 | } |
michael@0 | 10213 | return aCaretPos.forget(); |
michael@0 | 10214 | } |
michael@0 | 10215 | |
michael@0 | 10216 | NS_IMETHODIMP |
michael@0 | 10217 | nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos) |
michael@0 | 10218 | { |
michael@0 | 10219 | NS_ENSURE_ARG_POINTER(aCaretPos); |
michael@0 | 10220 | *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take(); |
michael@0 | 10221 | return NS_OK; |
michael@0 | 10222 | } |
michael@0 | 10223 | |
michael@0 | 10224 | void |
michael@0 | 10225 | nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv) |
michael@0 | 10226 | { |
michael@0 | 10227 | nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI); |
michael@0 | 10228 | if (NS_FAILED(res)) { |
michael@0 | 10229 | rv.Throw(res); |
michael@0 | 10230 | } |
michael@0 | 10231 | } |
michael@0 | 10232 | |
michael@0 | 10233 | void |
michael@0 | 10234 | nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv) |
michael@0 | 10235 | { |
michael@0 | 10236 | nsCOMPtr<nsIURI> uri; |
michael@0 | 10237 | nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI); |
michael@0 | 10238 | if (NS_FAILED(res)) { |
michael@0 | 10239 | rv.Throw(res); |
michael@0 | 10240 | return; |
michael@0 | 10241 | } |
michael@0 | 10242 | res = CSSLoader()->ObsoleteSheet(uri); |
michael@0 | 10243 | if (NS_FAILED(res)) { |
michael@0 | 10244 | rv.Throw(res); |
michael@0 | 10245 | } |
michael@0 | 10246 | } |
michael@0 | 10247 | |
michael@0 | 10248 | nsIHTMLCollection* |
michael@0 | 10249 | nsIDocument::Children() |
michael@0 | 10250 | { |
michael@0 | 10251 | if (!mChildrenCollection) { |
michael@0 | 10252 | mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard, |
michael@0 | 10253 | nsGkAtoms::_asterix, |
michael@0 | 10254 | nsGkAtoms::_asterix, |
michael@0 | 10255 | false); |
michael@0 | 10256 | } |
michael@0 | 10257 | |
michael@0 | 10258 | return mChildrenCollection; |
michael@0 | 10259 | } |
michael@0 | 10260 | |
michael@0 | 10261 | uint32_t |
michael@0 | 10262 | nsIDocument::ChildElementCount() |
michael@0 | 10263 | { |
michael@0 | 10264 | return Children()->Length(); |
michael@0 | 10265 | } |
michael@0 | 10266 | |
michael@0 | 10267 | namespace mozilla { |
michael@0 | 10268 | |
michael@0 | 10269 | // Singleton class to manage the list of fullscreen documents which are the |
michael@0 | 10270 | // root of a branch which contains fullscreen documents. We maintain this list |
michael@0 | 10271 | // so that we can easily exit all windows from fullscreen when the user |
michael@0 | 10272 | // presses the escape key. |
michael@0 | 10273 | class FullscreenRoots { |
michael@0 | 10274 | public: |
michael@0 | 10275 | // Adds a root to the manager. Adding a root multiple times does not result |
michael@0 | 10276 | // in duplicate entries for that item, only one. |
michael@0 | 10277 | static void Add(nsIDocument* aRoot); |
michael@0 | 10278 | |
michael@0 | 10279 | // Iterates over every root in the root list, and calls aFunction, passing |
michael@0 | 10280 | // each root once to aFunction. It is safe to call Add() and Remove() while |
michael@0 | 10281 | // iterating over the list (i.e. in aFunction). Documents that are removed |
michael@0 | 10282 | // from the manager during traversal are not traversed, and documents that |
michael@0 | 10283 | // are added to the manager during traversal are also not traversed. |
michael@0 | 10284 | static void ForEach(void(*aFunction)(nsIDocument* aDoc)); |
michael@0 | 10285 | |
michael@0 | 10286 | // Removes a specific root from the manager. |
michael@0 | 10287 | static void Remove(nsIDocument* aRoot); |
michael@0 | 10288 | |
michael@0 | 10289 | // Returns true if all roots added to the list have been removed. |
michael@0 | 10290 | static bool IsEmpty(); |
michael@0 | 10291 | |
michael@0 | 10292 | private: |
michael@0 | 10293 | |
michael@0 | 10294 | FullscreenRoots() { |
michael@0 | 10295 | MOZ_COUNT_CTOR(FullscreenRoots); |
michael@0 | 10296 | } |
michael@0 | 10297 | ~FullscreenRoots() { |
michael@0 | 10298 | MOZ_COUNT_DTOR(FullscreenRoots); |
michael@0 | 10299 | } |
michael@0 | 10300 | |
michael@0 | 10301 | enum { |
michael@0 | 10302 | NotFound = uint32_t(-1) |
michael@0 | 10303 | }; |
michael@0 | 10304 | // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound. |
michael@0 | 10305 | static uint32_t Find(nsIDocument* aRoot); |
michael@0 | 10306 | |
michael@0 | 10307 | // Returns true if aRoot is in the list of fullscreen roots. |
michael@0 | 10308 | static bool Contains(nsIDocument* aRoot); |
michael@0 | 10309 | |
michael@0 | 10310 | // Singleton instance of the FullscreenRoots. This is instantiated when a |
michael@0 | 10311 | // root is added, and it is deleted when the last root is removed. |
michael@0 | 10312 | static FullscreenRoots* sInstance; |
michael@0 | 10313 | |
michael@0 | 10314 | // List of weak pointers to roots. |
michael@0 | 10315 | nsTArray<nsWeakPtr> mRoots; |
michael@0 | 10316 | }; |
michael@0 | 10317 | |
michael@0 | 10318 | FullscreenRoots* FullscreenRoots::sInstance = nullptr; |
michael@0 | 10319 | |
michael@0 | 10320 | /* static */ |
michael@0 | 10321 | void |
michael@0 | 10322 | FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc)) |
michael@0 | 10323 | { |
michael@0 | 10324 | if (!sInstance) { |
michael@0 | 10325 | return; |
michael@0 | 10326 | } |
michael@0 | 10327 | // Create a copy of the roots array, and iterate over the copy. This is so |
michael@0 | 10328 | // that if an element is removed from mRoots we don't mess up our iteration. |
michael@0 | 10329 | nsTArray<nsWeakPtr> roots(sInstance->mRoots); |
michael@0 | 10330 | // Call aFunction on all entries. |
michael@0 | 10331 | for (uint32_t i = 0; i < roots.Length(); i++) { |
michael@0 | 10332 | nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]); |
michael@0 | 10333 | // Check that the root isn't in the manager. This is so that new additions |
michael@0 | 10334 | // while we were running don't get traversed. |
michael@0 | 10335 | if (root && FullscreenRoots::Contains(root)) { |
michael@0 | 10336 | aFunction(root); |
michael@0 | 10337 | } |
michael@0 | 10338 | } |
michael@0 | 10339 | } |
michael@0 | 10340 | |
michael@0 | 10341 | /* static */ |
michael@0 | 10342 | bool |
michael@0 | 10343 | FullscreenRoots::Contains(nsIDocument* aRoot) |
michael@0 | 10344 | { |
michael@0 | 10345 | return FullscreenRoots::Find(aRoot) != NotFound; |
michael@0 | 10346 | } |
michael@0 | 10347 | |
michael@0 | 10348 | /* static */ |
michael@0 | 10349 | void |
michael@0 | 10350 | FullscreenRoots::Add(nsIDocument* aRoot) |
michael@0 | 10351 | { |
michael@0 | 10352 | if (!FullscreenRoots::Contains(aRoot)) { |
michael@0 | 10353 | if (!sInstance) { |
michael@0 | 10354 | sInstance = new FullscreenRoots(); |
michael@0 | 10355 | } |
michael@0 | 10356 | nsWeakPtr weakRoot = do_GetWeakReference(aRoot); |
michael@0 | 10357 | sInstance->mRoots.AppendElement(weakRoot); |
michael@0 | 10358 | } |
michael@0 | 10359 | } |
michael@0 | 10360 | |
michael@0 | 10361 | /* static */ |
michael@0 | 10362 | uint32_t |
michael@0 | 10363 | FullscreenRoots::Find(nsIDocument* aRoot) |
michael@0 | 10364 | { |
michael@0 | 10365 | if (!sInstance) { |
michael@0 | 10366 | return NotFound; |
michael@0 | 10367 | } |
michael@0 | 10368 | nsTArray<nsWeakPtr>& roots = sInstance->mRoots; |
michael@0 | 10369 | for (uint32_t i = 0; i < roots.Length(); i++) { |
michael@0 | 10370 | nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i])); |
michael@0 | 10371 | if (otherRoot == aRoot) { |
michael@0 | 10372 | return i; |
michael@0 | 10373 | } |
michael@0 | 10374 | } |
michael@0 | 10375 | return NotFound; |
michael@0 | 10376 | } |
michael@0 | 10377 | |
michael@0 | 10378 | /* static */ |
michael@0 | 10379 | void |
michael@0 | 10380 | FullscreenRoots::Remove(nsIDocument* aRoot) |
michael@0 | 10381 | { |
michael@0 | 10382 | uint32_t index = Find(aRoot); |
michael@0 | 10383 | NS_ASSERTION(index != NotFound, |
michael@0 | 10384 | "Should only try to remove roots which are still added!"); |
michael@0 | 10385 | if (index == NotFound || !sInstance) { |
michael@0 | 10386 | return; |
michael@0 | 10387 | } |
michael@0 | 10388 | sInstance->mRoots.RemoveElementAt(index); |
michael@0 | 10389 | if (sInstance->mRoots.IsEmpty()) { |
michael@0 | 10390 | delete sInstance; |
michael@0 | 10391 | sInstance = nullptr; |
michael@0 | 10392 | } |
michael@0 | 10393 | } |
michael@0 | 10394 | |
michael@0 | 10395 | /* static */ |
michael@0 | 10396 | bool |
michael@0 | 10397 | FullscreenRoots::IsEmpty() |
michael@0 | 10398 | { |
michael@0 | 10399 | return !sInstance; |
michael@0 | 10400 | } |
michael@0 | 10401 | |
michael@0 | 10402 | } // end namespace mozilla. |
michael@0 | 10403 | using mozilla::FullscreenRoots; |
michael@0 | 10404 | |
michael@0 | 10405 | nsIDocument* |
michael@0 | 10406 | nsDocument::GetFullscreenRoot() |
michael@0 | 10407 | { |
michael@0 | 10408 | nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot); |
michael@0 | 10409 | return root; |
michael@0 | 10410 | } |
michael@0 | 10411 | |
michael@0 | 10412 | void |
michael@0 | 10413 | nsDocument::SetFullscreenRoot(nsIDocument* aRoot) |
michael@0 | 10414 | { |
michael@0 | 10415 | mFullscreenRoot = do_GetWeakReference(aRoot); |
michael@0 | 10416 | } |
michael@0 | 10417 | |
michael@0 | 10418 | NS_IMETHODIMP |
michael@0 | 10419 | nsDocument::MozCancelFullScreen() |
michael@0 | 10420 | { |
michael@0 | 10421 | nsIDocument::MozCancelFullScreen(); |
michael@0 | 10422 | return NS_OK; |
michael@0 | 10423 | } |
michael@0 | 10424 | |
michael@0 | 10425 | void |
michael@0 | 10426 | nsIDocument::MozCancelFullScreen() |
michael@0 | 10427 | { |
michael@0 | 10428 | RestorePreviousFullScreenState(); |
michael@0 | 10429 | } |
michael@0 | 10430 | |
michael@0 | 10431 | // Runnable to set window full-screen mode. Used as a script runner |
michael@0 | 10432 | // to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to |
michael@0 | 10433 | // run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event |
michael@0 | 10434 | // (handled in chome code) which is unsafe to run if this is called in |
michael@0 | 10435 | // Element::UnbindFromTree(). |
michael@0 | 10436 | class nsSetWindowFullScreen : public nsRunnable { |
michael@0 | 10437 | public: |
michael@0 | 10438 | nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue) |
michael@0 | 10439 | : mDoc(aDoc), mValue(aValue) {} |
michael@0 | 10440 | |
michael@0 | 10441 | NS_IMETHOD Run() |
michael@0 | 10442 | { |
michael@0 | 10443 | if (mDoc->GetWindow()) { |
michael@0 | 10444 | mDoc->GetWindow()->SetFullScreenInternal(mValue, false); |
michael@0 | 10445 | } |
michael@0 | 10446 | return NS_OK; |
michael@0 | 10447 | } |
michael@0 | 10448 | |
michael@0 | 10449 | private: |
michael@0 | 10450 | nsCOMPtr<nsIDocument> mDoc; |
michael@0 | 10451 | bool mValue; |
michael@0 | 10452 | }; |
michael@0 | 10453 | |
michael@0 | 10454 | static nsIDocument* |
michael@0 | 10455 | GetFullscreenRootDocument(nsIDocument* aDoc) |
michael@0 | 10456 | { |
michael@0 | 10457 | if (!aDoc) { |
michael@0 | 10458 | return nullptr; |
michael@0 | 10459 | } |
michael@0 | 10460 | nsIDocument* doc = aDoc; |
michael@0 | 10461 | nsIDocument* parent; |
michael@0 | 10462 | while ((parent = doc->GetParentDocument()) && |
michael@0 | 10463 | (!nsContentUtils::IsFullscreenApiContentOnly() || |
michael@0 | 10464 | !nsContentUtils::IsChromeDoc(parent))) { |
michael@0 | 10465 | doc = parent; |
michael@0 | 10466 | } |
michael@0 | 10467 | return doc; |
michael@0 | 10468 | } |
michael@0 | 10469 | |
michael@0 | 10470 | static void |
michael@0 | 10471 | SetWindowFullScreen(nsIDocument* aDoc, bool aValue) |
michael@0 | 10472 | { |
michael@0 | 10473 | // Maintain list of fullscreen root documents. |
michael@0 | 10474 | nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc); |
michael@0 | 10475 | if (aValue) { |
michael@0 | 10476 | FullscreenRoots::Add(root); |
michael@0 | 10477 | } else { |
michael@0 | 10478 | FullscreenRoots::Remove(root); |
michael@0 | 10479 | } |
michael@0 | 10480 | if (!nsContentUtils::IsFullscreenApiContentOnly()) { |
michael@0 | 10481 | nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue)); |
michael@0 | 10482 | } |
michael@0 | 10483 | } |
michael@0 | 10484 | |
michael@0 | 10485 | class nsCallExitFullscreen : public nsRunnable { |
michael@0 | 10486 | public: |
michael@0 | 10487 | nsCallExitFullscreen(nsIDocument* aDoc) |
michael@0 | 10488 | : mDoc(aDoc) {} |
michael@0 | 10489 | NS_IMETHOD Run() |
michael@0 | 10490 | { |
michael@0 | 10491 | nsDocument::ExitFullscreen(mDoc); |
michael@0 | 10492 | return NS_OK; |
michael@0 | 10493 | } |
michael@0 | 10494 | private: |
michael@0 | 10495 | nsCOMPtr<nsIDocument> mDoc; |
michael@0 | 10496 | }; |
michael@0 | 10497 | |
michael@0 | 10498 | /* static */ |
michael@0 | 10499 | void |
michael@0 | 10500 | nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync) |
michael@0 | 10501 | { |
michael@0 | 10502 | if (aDoc && !aDoc->IsFullScreenDoc()) { |
michael@0 | 10503 | return; |
michael@0 | 10504 | } |
michael@0 | 10505 | if (aRunAsync) { |
michael@0 | 10506 | NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc)); |
michael@0 | 10507 | return; |
michael@0 | 10508 | } |
michael@0 | 10509 | nsDocument::ExitFullscreen(aDoc); |
michael@0 | 10510 | } |
michael@0 | 10511 | |
michael@0 | 10512 | // Returns true if the document is a direct child of a cross process parent |
michael@0 | 10513 | // mozbrowser iframe. This is the case when the document has a null parent, |
michael@0 | 10514 | // and its DocShell reports that it is a browser frame. |
michael@0 | 10515 | static bool |
michael@0 | 10516 | HasCrossProcessParent(nsIDocument* aDocument) |
michael@0 | 10517 | { |
michael@0 | 10518 | if (XRE_GetProcessType() != GeckoProcessType_Content) { |
michael@0 | 10519 | return false; |
michael@0 | 10520 | } |
michael@0 | 10521 | if (aDocument->GetParentDocument() != nullptr) { |
michael@0 | 10522 | return false; |
michael@0 | 10523 | } |
michael@0 | 10524 | nsPIDOMWindow* win = aDocument->GetWindow(); |
michael@0 | 10525 | if (!win) { |
michael@0 | 10526 | return false; |
michael@0 | 10527 | } |
michael@0 | 10528 | nsCOMPtr<nsIDocShell> docShell = win->GetDocShell(); |
michael@0 | 10529 | if (!docShell) { |
michael@0 | 10530 | return false; |
michael@0 | 10531 | } |
michael@0 | 10532 | return docShell->GetIsBrowserOrApp(); |
michael@0 | 10533 | } |
michael@0 | 10534 | |
michael@0 | 10535 | static bool |
michael@0 | 10536 | CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) |
michael@0 | 10537 | { |
michael@0 | 10538 | if (aDoc->IsFullScreenDoc()) { |
michael@0 | 10539 | uint32_t* count = static_cast<uint32_t*>(aData); |
michael@0 | 10540 | (*count)++; |
michael@0 | 10541 | } |
michael@0 | 10542 | return true; |
michael@0 | 10543 | } |
michael@0 | 10544 | |
michael@0 | 10545 | static uint32_t |
michael@0 | 10546 | CountFullscreenSubDocuments(nsIDocument* aDoc) |
michael@0 | 10547 | { |
michael@0 | 10548 | uint32_t count = 0; |
michael@0 | 10549 | aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count); |
michael@0 | 10550 | return count; |
michael@0 | 10551 | } |
michael@0 | 10552 | |
michael@0 | 10553 | bool |
michael@0 | 10554 | nsDocument::IsFullscreenLeaf() |
michael@0 | 10555 | { |
michael@0 | 10556 | // A fullscreen leaf document is fullscreen, and has no fullscreen |
michael@0 | 10557 | // subdocuments. |
michael@0 | 10558 | if (!IsFullScreenDoc()) { |
michael@0 | 10559 | return false; |
michael@0 | 10560 | } |
michael@0 | 10561 | return CountFullscreenSubDocuments(this) == 0; |
michael@0 | 10562 | } |
michael@0 | 10563 | |
michael@0 | 10564 | static bool |
michael@0 | 10565 | ResetFullScreen(nsIDocument* aDocument, void* aData) |
michael@0 | 10566 | { |
michael@0 | 10567 | if (aDocument->IsFullScreenDoc()) { |
michael@0 | 10568 | NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1, |
michael@0 | 10569 | "Should have at most 1 fullscreen subdocument."); |
michael@0 | 10570 | static_cast<nsDocument*>(aDocument)->CleanupFullscreenState(); |
michael@0 | 10571 | NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen"); |
michael@0 | 10572 | nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData); |
michael@0 | 10573 | changed->AppendElement(aDocument); |
michael@0 | 10574 | |
michael@0 | 10575 | if (HasCrossProcessParent(aDocument)) { |
michael@0 | 10576 | // We're at the top of the content-process side doc tree. Ask the parent |
michael@0 | 10577 | // process to exit fullscreen. |
michael@0 | 10578 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10579 | os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr); |
michael@0 | 10580 | } |
michael@0 | 10581 | |
michael@0 | 10582 | // Dispatch a notification so that if this document has any |
michael@0 | 10583 | // cross-process subdocuments, they'll be notified to exit fullscreen. |
michael@0 | 10584 | // The BrowserElementParent listens for this event and performs the |
michael@0 | 10585 | // cross process notification if it has a remote child process. |
michael@0 | 10586 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10587 | os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr); |
michael@0 | 10588 | |
michael@0 | 10589 | aDocument->EnumerateSubDocuments(ResetFullScreen, aData); |
michael@0 | 10590 | } |
michael@0 | 10591 | return true; |
michael@0 | 10592 | } |
michael@0 | 10593 | |
michael@0 | 10594 | static void |
michael@0 | 10595 | ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc) |
michael@0 | 10596 | { |
michael@0 | 10597 | MOZ_ASSERT(aMaybeNotARootDoc); |
michael@0 | 10598 | nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot(); |
michael@0 | 10599 | NS_ASSERTION(root, "Should have root when in fullscreen!"); |
michael@0 | 10600 | if (!root) { |
michael@0 | 10601 | return; |
michael@0 | 10602 | } |
michael@0 | 10603 | NS_ASSERTION(root->IsFullScreenDoc(), |
michael@0 | 10604 | "Fullscreen root should be a fullscreen doc..."); |
michael@0 | 10605 | |
michael@0 | 10606 | // Stores a list of documents to which we must dispatch "mozfullscreenchange". |
michael@0 | 10607 | // We're required by the spec to dispatch the events in leaf-to-root |
michael@0 | 10608 | // order when exiting fullscreen, but we traverse the doctree in a |
michael@0 | 10609 | // root-to-leaf order, so we save references to the documents we must |
michael@0 | 10610 | // dispatch to so that we dispatch in the specified order. |
michael@0 | 10611 | nsAutoTArray<nsIDocument*, 8> changed; |
michael@0 | 10612 | |
michael@0 | 10613 | // Walk the tree of fullscreen documents, and reset their fullscreen state. |
michael@0 | 10614 | ResetFullScreen(root, static_cast<void*>(&changed)); |
michael@0 | 10615 | |
michael@0 | 10616 | // Dispatch "mozfullscreenchange" events. Note this loop is in reverse |
michael@0 | 10617 | // order so that the events for the leaf document arrives before the root |
michael@0 | 10618 | // document, as required by the spec. |
michael@0 | 10619 | for (uint32_t i = 0; i < changed.Length(); ++i) { |
michael@0 | 10620 | DispatchFullScreenChange(changed[changed.Length() - i - 1]); |
michael@0 | 10621 | } |
michael@0 | 10622 | |
michael@0 | 10623 | NS_ASSERTION(!root->IsFullScreenDoc(), |
michael@0 | 10624 | "Fullscreen root should no longer be a fullscreen doc..."); |
michael@0 | 10625 | |
michael@0 | 10626 | // Move the top-level window out of fullscreen mode. |
michael@0 | 10627 | SetWindowFullScreen(root, false); |
michael@0 | 10628 | } |
michael@0 | 10629 | |
michael@0 | 10630 | /* static */ |
michael@0 | 10631 | void |
michael@0 | 10632 | nsDocument::ExitFullscreen(nsIDocument* aDoc) |
michael@0 | 10633 | { |
michael@0 | 10634 | // Unlock the pointer, if it's locked. |
michael@0 | 10635 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 10636 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 10637 | if (pointerLockedElement) { |
michael@0 | 10638 | UnlockPointer(); |
michael@0 | 10639 | } |
michael@0 | 10640 | |
michael@0 | 10641 | if (aDoc) { |
michael@0 | 10642 | ExitFullscreenInDocTree(aDoc); |
michael@0 | 10643 | return; |
michael@0 | 10644 | } |
michael@0 | 10645 | |
michael@0 | 10646 | // Clear fullscreen stacks in all fullscreen roots' descendant documents. |
michael@0 | 10647 | FullscreenRoots::ForEach(&ExitFullscreenInDocTree); |
michael@0 | 10648 | NS_ASSERTION(FullscreenRoots::IsEmpty(), |
michael@0 | 10649 | "Should have exited all fullscreen roots from fullscreen"); |
michael@0 | 10650 | } |
michael@0 | 10651 | |
michael@0 | 10652 | bool |
michael@0 | 10653 | GetFullscreenLeaf(nsIDocument* aDoc, void* aData) |
michael@0 | 10654 | { |
michael@0 | 10655 | if (aDoc->IsFullscreenLeaf()) { |
michael@0 | 10656 | nsIDocument** result = static_cast<nsIDocument**>(aData); |
michael@0 | 10657 | *result = aDoc; |
michael@0 | 10658 | return false; |
michael@0 | 10659 | } else if (aDoc->IsFullScreenDoc()) { |
michael@0 | 10660 | aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData); |
michael@0 | 10661 | } |
michael@0 | 10662 | return true; |
michael@0 | 10663 | } |
michael@0 | 10664 | |
michael@0 | 10665 | static nsIDocument* |
michael@0 | 10666 | GetFullscreenLeaf(nsIDocument* aDoc) |
michael@0 | 10667 | { |
michael@0 | 10668 | nsIDocument* leaf = nullptr; |
michael@0 | 10669 | GetFullscreenLeaf(aDoc, &leaf); |
michael@0 | 10670 | if (leaf) { |
michael@0 | 10671 | return leaf; |
michael@0 | 10672 | } |
michael@0 | 10673 | // Otherwise we could be either in a non-fullscreen doc tree, or we're |
michael@0 | 10674 | // below the fullscreen doc. Start the search from the root. |
michael@0 | 10675 | nsIDocument* root = GetFullscreenRootDocument(aDoc); |
michael@0 | 10676 | // Check that the root is actually fullscreen so we don't waste time walking |
michael@0 | 10677 | // around its descendants. |
michael@0 | 10678 | if (!root->IsFullScreenDoc()) { |
michael@0 | 10679 | return nullptr; |
michael@0 | 10680 | } |
michael@0 | 10681 | GetFullscreenLeaf(root, &leaf); |
michael@0 | 10682 | return leaf; |
michael@0 | 10683 | } |
michael@0 | 10684 | |
michael@0 | 10685 | void |
michael@0 | 10686 | nsDocument::RestorePreviousFullScreenState() |
michael@0 | 10687 | { |
michael@0 | 10688 | NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(), |
michael@0 | 10689 | "Should have at least 1 fullscreen root when fullscreen!"); |
michael@0 | 10690 | NS_ASSERTION(!nsContentUtils::IsFullscreenApiContentOnly() || |
michael@0 | 10691 | !nsContentUtils::IsChromeDoc(this), |
michael@0 | 10692 | "Should not run RestorePreviousFullScreenState() on " |
michael@0 | 10693 | "chrome document when fullscreen is content only"); |
michael@0 | 10694 | |
michael@0 | 10695 | if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) { |
michael@0 | 10696 | return; |
michael@0 | 10697 | } |
michael@0 | 10698 | |
michael@0 | 10699 | // If fullscreen mode is updated the pointer should be unlocked |
michael@0 | 10700 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 10701 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 10702 | if (pointerLockedElement) { |
michael@0 | 10703 | UnlockPointer(); |
michael@0 | 10704 | } |
michael@0 | 10705 | |
michael@0 | 10706 | nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this); |
michael@0 | 10707 | |
michael@0 | 10708 | // The fullscreen document may contain a <iframe mozbrowser> element which |
michael@0 | 10709 | // has a cross process child. So send a notification so that its browser |
michael@0 | 10710 | // parent will send a message to its child process to also exit fullscreen. |
michael@0 | 10711 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10712 | os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr); |
michael@0 | 10713 | |
michael@0 | 10714 | // Clear full-screen stacks in all descendant in process documents, bottom up. |
michael@0 | 10715 | nsIDocument* doc = fullScreenDoc; |
michael@0 | 10716 | while (doc != this) { |
michael@0 | 10717 | NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc"); |
michael@0 | 10718 | static_cast<nsDocument*>(doc)->CleanupFullscreenState(); |
michael@0 | 10719 | UnlockPointer(); |
michael@0 | 10720 | DispatchFullScreenChange(doc); |
michael@0 | 10721 | doc = doc->GetParentDocument(); |
michael@0 | 10722 | } |
michael@0 | 10723 | |
michael@0 | 10724 | // Roll-back full-screen state to previous full-screen element. |
michael@0 | 10725 | NS_ASSERTION(doc == this, "Must have reached this doc."); |
michael@0 | 10726 | while (doc != nullptr) { |
michael@0 | 10727 | static_cast<nsDocument*>(doc)->FullScreenStackPop(); |
michael@0 | 10728 | UnlockPointer(); |
michael@0 | 10729 | DispatchFullScreenChange(doc); |
michael@0 | 10730 | if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) { |
michael@0 | 10731 | if (HasCrossProcessParent(doc)) { |
michael@0 | 10732 | // Send notification to the parent process to tell it to rollback to |
michael@0 | 10733 | // the previous fullscreen elements in its fullscreen element stacks. |
michael@0 | 10734 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10735 | os->NotifyObservers(doc, "ask-parent-to-rollback-fullscreen", nullptr); |
michael@0 | 10736 | } |
michael@0 | 10737 | // Full-screen stack in document is empty. Go back up to the parent |
michael@0 | 10738 | // document. We'll pop the containing element off its stack, and use |
michael@0 | 10739 | // its next full-screen element as the full-screen element. |
michael@0 | 10740 | static_cast<nsDocument*>(doc)->CleanupFullscreenState(); |
michael@0 | 10741 | doc = doc->GetParentDocument(); |
michael@0 | 10742 | } else { |
michael@0 | 10743 | // Else we popped the top of the stack, and there's still another |
michael@0 | 10744 | // element in there, so that will become the full-screen element. |
michael@0 | 10745 | if (fullScreenDoc != doc) { |
michael@0 | 10746 | // We've popped so enough off the stack that we've rolled back to |
michael@0 | 10747 | // a fullscreen element in a parent document. If this document isn't |
michael@0 | 10748 | // approved for fullscreen, or if it's cross origin, dispatch an |
michael@0 | 10749 | // event to chrome so it knows to show the authorization/warning UI. |
michael@0 | 10750 | if (!nsContentUtils::HaveEqualPrincipals(fullScreenDoc, doc) || |
michael@0 | 10751 | (!nsContentUtils::IsSitePermAllow(doc->NodePrincipal(), "fullscreen") && |
michael@0 | 10752 | !static_cast<nsDocument*>(doc)->mIsApprovedForFullscreen)) { |
michael@0 | 10753 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 10754 | new AsyncEventDispatcher(doc, |
michael@0 | 10755 | NS_LITERAL_STRING("MozEnteredDomFullscreen"), |
michael@0 | 10756 | true, |
michael@0 | 10757 | true); |
michael@0 | 10758 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 10759 | } |
michael@0 | 10760 | } |
michael@0 | 10761 | |
michael@0 | 10762 | if (!nsContentUtils::HaveEqualPrincipals(doc, fullScreenDoc)) { |
michael@0 | 10763 | // The origin which is fullscreen changed. Send a notification to |
michael@0 | 10764 | // the root process so that a warning or approval UI can be shown |
michael@0 | 10765 | // as necessary. |
michael@0 | 10766 | nsAutoString origin; |
michael@0 | 10767 | nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin); |
michael@0 | 10768 | nsIDocument* root = GetFullscreenRootDocument(doc); |
michael@0 | 10769 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10770 | os->NotifyObservers(root, "fullscreen-origin-change", origin.get()); |
michael@0 | 10771 | } |
michael@0 | 10772 | |
michael@0 | 10773 | break; |
michael@0 | 10774 | } |
michael@0 | 10775 | } |
michael@0 | 10776 | |
michael@0 | 10777 | if (doc == nullptr) { |
michael@0 | 10778 | // We moved all documents in this doctree out of fullscreen mode, |
michael@0 | 10779 | // move the top-level window out of fullscreen mode. |
michael@0 | 10780 | NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(), |
michael@0 | 10781 | "Should have cleared all docs' stacks"); |
michael@0 | 10782 | SetWindowFullScreen(this, false); |
michael@0 | 10783 | } |
michael@0 | 10784 | } |
michael@0 | 10785 | |
michael@0 | 10786 | bool |
michael@0 | 10787 | nsDocument::IsFullScreenDoc() |
michael@0 | 10788 | { |
michael@0 | 10789 | return GetFullScreenElement() != nullptr; |
michael@0 | 10790 | } |
michael@0 | 10791 | |
michael@0 | 10792 | class nsCallRequestFullScreen : public nsRunnable |
michael@0 | 10793 | { |
michael@0 | 10794 | public: |
michael@0 | 10795 | nsCallRequestFullScreen(Element* aElement) |
michael@0 | 10796 | : mElement(aElement), |
michael@0 | 10797 | mDoc(aElement->OwnerDoc()), |
michael@0 | 10798 | mWasCallerChrome(nsContentUtils::IsCallerChrome()), |
michael@0 | 10799 | mHadRequestPending(static_cast<nsDocument*>(mDoc.get())-> |
michael@0 | 10800 | mAsyncFullscreenPending) |
michael@0 | 10801 | { |
michael@0 | 10802 | static_cast<nsDocument*>(mDoc.get())-> |
michael@0 | 10803 | mAsyncFullscreenPending = true; |
michael@0 | 10804 | } |
michael@0 | 10805 | |
michael@0 | 10806 | NS_IMETHOD Run() |
michael@0 | 10807 | { |
michael@0 | 10808 | static_cast<nsDocument*>(mDoc.get())-> |
michael@0 | 10809 | mAsyncFullscreenPending = mHadRequestPending; |
michael@0 | 10810 | nsDocument* doc = static_cast<nsDocument*>(mDoc.get()); |
michael@0 | 10811 | doc->RequestFullScreen(mElement, |
michael@0 | 10812 | mWasCallerChrome, |
michael@0 | 10813 | /* aNotifyOnOriginChange */ true); |
michael@0 | 10814 | return NS_OK; |
michael@0 | 10815 | } |
michael@0 | 10816 | |
michael@0 | 10817 | nsRefPtr<Element> mElement; |
michael@0 | 10818 | nsCOMPtr<nsIDocument> mDoc; |
michael@0 | 10819 | bool mWasCallerChrome; |
michael@0 | 10820 | bool mHadRequestPending; |
michael@0 | 10821 | }; |
michael@0 | 10822 | |
michael@0 | 10823 | void |
michael@0 | 10824 | nsDocument::AsyncRequestFullScreen(Element* aElement) |
michael@0 | 10825 | { |
michael@0 | 10826 | NS_ASSERTION(aElement, |
michael@0 | 10827 | "Must pass non-null element to nsDocument::AsyncRequestFullScreen"); |
michael@0 | 10828 | if (!aElement) { |
michael@0 | 10829 | return; |
michael@0 | 10830 | } |
michael@0 | 10831 | // Request full-screen asynchronously. |
michael@0 | 10832 | nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement)); |
michael@0 | 10833 | NS_DispatchToCurrentThread(event); |
michael@0 | 10834 | } |
michael@0 | 10835 | |
michael@0 | 10836 | static void |
michael@0 | 10837 | LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc) |
michael@0 | 10838 | { |
michael@0 | 10839 | if (!aLogFailure) { |
michael@0 | 10840 | return; |
michael@0 | 10841 | } |
michael@0 | 10842 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 10843 | new AsyncEventDispatcher(aDoc, |
michael@0 | 10844 | NS_LITERAL_STRING("mozfullscreenerror"), |
michael@0 | 10845 | true, |
michael@0 | 10846 | false); |
michael@0 | 10847 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 10848 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 10849 | NS_LITERAL_CSTRING("DOM"), aDoc, |
michael@0 | 10850 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10851 | aMessage); |
michael@0 | 10852 | } |
michael@0 | 10853 | |
michael@0 | 10854 | nsresult |
michael@0 | 10855 | nsDocument::AddFullscreenApprovedObserver() |
michael@0 | 10856 | { |
michael@0 | 10857 | if (mHasFullscreenApprovedObserver || |
michael@0 | 10858 | !Preferences::GetBool("full-screen-api.approval-required")) { |
michael@0 | 10859 | return NS_OK; |
michael@0 | 10860 | } |
michael@0 | 10861 | |
michael@0 | 10862 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10863 | NS_ENSURE_TRUE(os, NS_ERROR_FAILURE); |
michael@0 | 10864 | |
michael@0 | 10865 | nsresult res = os->AddObserver(this, "fullscreen-approved", true); |
michael@0 | 10866 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 10867 | |
michael@0 | 10868 | mHasFullscreenApprovedObserver = true; |
michael@0 | 10869 | |
michael@0 | 10870 | return NS_OK; |
michael@0 | 10871 | } |
michael@0 | 10872 | |
michael@0 | 10873 | nsresult |
michael@0 | 10874 | nsDocument::RemoveFullscreenApprovedObserver() |
michael@0 | 10875 | { |
michael@0 | 10876 | if (!mHasFullscreenApprovedObserver) { |
michael@0 | 10877 | return NS_OK; |
michael@0 | 10878 | } |
michael@0 | 10879 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 10880 | NS_ENSURE_TRUE(os, NS_ERROR_FAILURE); |
michael@0 | 10881 | |
michael@0 | 10882 | nsresult res = os->RemoveObserver(this, "fullscreen-approved"); |
michael@0 | 10883 | NS_ENSURE_SUCCESS(res, res); |
michael@0 | 10884 | |
michael@0 | 10885 | mHasFullscreenApprovedObserver = false; |
michael@0 | 10886 | |
michael@0 | 10887 | return NS_OK; |
michael@0 | 10888 | } |
michael@0 | 10889 | |
michael@0 | 10890 | void |
michael@0 | 10891 | nsDocument::CleanupFullscreenState() |
michael@0 | 10892 | { |
michael@0 | 10893 | if (!mFullScreenStack.IsEmpty()) { |
michael@0 | 10894 | // The top element in the full-screen stack will have full-screen |
michael@0 | 10895 | // style bits set on it and its ancestors. Remove the style bits. |
michael@0 | 10896 | // Note the non-top elements won't have the style bits set. |
michael@0 | 10897 | Element* top = FullScreenStackTop(); |
michael@0 | 10898 | NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty"); |
michael@0 | 10899 | if (top) { |
michael@0 | 10900 | EventStateManager::SetFullScreenState(top, false); |
michael@0 | 10901 | } |
michael@0 | 10902 | mFullScreenStack.Clear(); |
michael@0 | 10903 | } |
michael@0 | 10904 | SetApprovedForFullscreen(false); |
michael@0 | 10905 | RemoveFullscreenApprovedObserver(); |
michael@0 | 10906 | mFullscreenRoot = nullptr; |
michael@0 | 10907 | } |
michael@0 | 10908 | |
michael@0 | 10909 | bool |
michael@0 | 10910 | nsDocument::FullScreenStackPush(Element* aElement) |
michael@0 | 10911 | { |
michael@0 | 10912 | NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()"); |
michael@0 | 10913 | Element* top = FullScreenStackTop(); |
michael@0 | 10914 | if (top == aElement || !aElement) { |
michael@0 | 10915 | return false; |
michael@0 | 10916 | } |
michael@0 | 10917 | if (top) { |
michael@0 | 10918 | // We're pushing a new element onto the full-screen stack, so we must |
michael@0 | 10919 | // remove the ancestor and full-screen styles from the former top of the |
michael@0 | 10920 | // stack. |
michael@0 | 10921 | EventStateManager::SetFullScreenState(top, false); |
michael@0 | 10922 | } |
michael@0 | 10923 | EventStateManager::SetFullScreenState(aElement, true); |
michael@0 | 10924 | nsWeakPtr weakElement = do_GetWeakReference(aElement); |
michael@0 | 10925 | mFullScreenStack.AppendElement(weakElement); |
michael@0 | 10926 | NS_ASSERTION(GetFullScreenElement() == aElement, "Should match"); |
michael@0 | 10927 | return true; |
michael@0 | 10928 | } |
michael@0 | 10929 | |
michael@0 | 10930 | void |
michael@0 | 10931 | nsDocument::FullScreenStackPop() |
michael@0 | 10932 | { |
michael@0 | 10933 | if (mFullScreenStack.IsEmpty()) { |
michael@0 | 10934 | return; |
michael@0 | 10935 | } |
michael@0 | 10936 | |
michael@0 | 10937 | // Remove styles from existing top element. |
michael@0 | 10938 | Element* top = FullScreenStackTop(); |
michael@0 | 10939 | EventStateManager::SetFullScreenState(top, false); |
michael@0 | 10940 | |
michael@0 | 10941 | // Remove top element. Note the remaining top element in the stack |
michael@0 | 10942 | // will not have full-screen style bits set, so we will need to restore |
michael@0 | 10943 | // them on the new top element before returning. |
michael@0 | 10944 | uint32_t last = mFullScreenStack.Length() - 1; |
michael@0 | 10945 | mFullScreenStack.RemoveElementAt(last); |
michael@0 | 10946 | |
michael@0 | 10947 | // Pop from the stack null elements (references to elements which have |
michael@0 | 10948 | // been GC'd since they were added to the stack) and elements which are |
michael@0 | 10949 | // no longer in this document. |
michael@0 | 10950 | while (!mFullScreenStack.IsEmpty()) { |
michael@0 | 10951 | Element* element = FullScreenStackTop(); |
michael@0 | 10952 | if (!element || !element->IsInDoc() || element->OwnerDoc() != this) { |
michael@0 | 10953 | NS_ASSERTION(!element->IsFullScreenAncestor(), |
michael@0 | 10954 | "Should have already removed full-screen styles"); |
michael@0 | 10955 | uint32_t last = mFullScreenStack.Length() - 1; |
michael@0 | 10956 | mFullScreenStack.RemoveElementAt(last); |
michael@0 | 10957 | } else { |
michael@0 | 10958 | // The top element of the stack is now an in-doc element. Apply the |
michael@0 | 10959 | // full-screen styles and return. |
michael@0 | 10960 | EventStateManager::SetFullScreenState(element, true); |
michael@0 | 10961 | break; |
michael@0 | 10962 | } |
michael@0 | 10963 | } |
michael@0 | 10964 | } |
michael@0 | 10965 | |
michael@0 | 10966 | Element* |
michael@0 | 10967 | nsDocument::FullScreenStackTop() |
michael@0 | 10968 | { |
michael@0 | 10969 | if (mFullScreenStack.IsEmpty()) { |
michael@0 | 10970 | return nullptr; |
michael@0 | 10971 | } |
michael@0 | 10972 | uint32_t last = mFullScreenStack.Length() - 1; |
michael@0 | 10973 | nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last])); |
michael@0 | 10974 | NS_ASSERTION(element, "Should have full-screen element!"); |
michael@0 | 10975 | NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc"); |
michael@0 | 10976 | NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc"); |
michael@0 | 10977 | return element; |
michael@0 | 10978 | } |
michael@0 | 10979 | |
michael@0 | 10980 | // Returns true if aDoc is in the focused tab in the active window. |
michael@0 | 10981 | static bool |
michael@0 | 10982 | IsInActiveTab(nsIDocument* aDoc) |
michael@0 | 10983 | { |
michael@0 | 10984 | nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell(); |
michael@0 | 10985 | if (!docshell) { |
michael@0 | 10986 | return false; |
michael@0 | 10987 | } |
michael@0 | 10988 | |
michael@0 | 10989 | bool isActive = false; |
michael@0 | 10990 | docshell->GetIsActive(&isActive); |
michael@0 | 10991 | if (!isActive) { |
michael@0 | 10992 | return false; |
michael@0 | 10993 | } |
michael@0 | 10994 | |
michael@0 | 10995 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
michael@0 | 10996 | docshell->GetRootTreeItem(getter_AddRefs(rootItem)); |
michael@0 | 10997 | if (!rootItem) { |
michael@0 | 10998 | return false; |
michael@0 | 10999 | } |
michael@0 | 11000 | nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem); |
michael@0 | 11001 | if (!rootWin) { |
michael@0 | 11002 | return false; |
michael@0 | 11003 | } |
michael@0 | 11004 | |
michael@0 | 11005 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 11006 | if (!fm) { |
michael@0 | 11007 | return false; |
michael@0 | 11008 | } |
michael@0 | 11009 | |
michael@0 | 11010 | nsCOMPtr<nsIDOMWindow> activeWindow; |
michael@0 | 11011 | fm->GetActiveWindow(getter_AddRefs(activeWindow)); |
michael@0 | 11012 | if (!activeWindow) { |
michael@0 | 11013 | return false; |
michael@0 | 11014 | } |
michael@0 | 11015 | |
michael@0 | 11016 | return activeWindow == rootWin; |
michael@0 | 11017 | } |
michael@0 | 11018 | |
michael@0 | 11019 | nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, |
michael@0 | 11020 | const nsAString& aOrigin) |
michael@0 | 11021 | { |
michael@0 | 11022 | // Ensure the frame element is the fullscreen element in this document. |
michael@0 | 11023 | // If the frame element is already the fullscreen element in this document, |
michael@0 | 11024 | // this has no effect. |
michael@0 | 11025 | nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement)); |
michael@0 | 11026 | RequestFullScreen(content->AsElement(), |
michael@0 | 11027 | /* aWasCallerChrome */ false, |
michael@0 | 11028 | /* aNotifyOnOriginChange */ false); |
michael@0 | 11029 | |
michael@0 | 11030 | // Origin changed in child process, send notifiction, so that chrome can |
michael@0 | 11031 | // update the UI to reflect the fullscreen origin change if necessary. |
michael@0 | 11032 | // The BrowserElementChild listens on this, and forwards it over its |
michael@0 | 11033 | // parent process, where it is redispatched. Chrome (in the root process, |
michael@0 | 11034 | // which could be *this* process) listens for this notification so that |
michael@0 | 11035 | // it can show a warning or approval UI. |
michael@0 | 11036 | if (!aOrigin.IsEmpty()) { |
michael@0 | 11037 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 11038 | os->NotifyObservers(GetFullscreenRootDocument(this), |
michael@0 | 11039 | "fullscreen-origin-change", |
michael@0 | 11040 | PromiseFlatString(aOrigin).get()); |
michael@0 | 11041 | } |
michael@0 | 11042 | |
michael@0 | 11043 | return NS_OK; |
michael@0 | 11044 | } |
michael@0 | 11045 | |
michael@0 | 11046 | nsresult nsDocument::RemoteFrameFullscreenReverted() |
michael@0 | 11047 | { |
michael@0 | 11048 | RestorePreviousFullScreenState(); |
michael@0 | 11049 | return NS_OK; |
michael@0 | 11050 | } |
michael@0 | 11051 | |
michael@0 | 11052 | void |
michael@0 | 11053 | nsDocument::RequestFullScreen(Element* aElement, |
michael@0 | 11054 | bool aWasCallerChrome, |
michael@0 | 11055 | bool aNotifyOnOriginChange) |
michael@0 | 11056 | { |
michael@0 | 11057 | NS_ASSERTION(aElement, |
michael@0 | 11058 | "Must pass non-null element to nsDocument::RequestFullScreen"); |
michael@0 | 11059 | if (!aElement || aElement == GetFullScreenElement()) { |
michael@0 | 11060 | return; |
michael@0 | 11061 | } |
michael@0 | 11062 | if (!aElement->IsInDoc()) { |
michael@0 | 11063 | LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this); |
michael@0 | 11064 | return; |
michael@0 | 11065 | } |
michael@0 | 11066 | if (aElement->OwnerDoc() != this) { |
michael@0 | 11067 | LogFullScreenDenied(true, "FullScreenDeniedMovedDocument", this); |
michael@0 | 11068 | return; |
michael@0 | 11069 | } |
michael@0 | 11070 | if (!GetWindow()) { |
michael@0 | 11071 | LogFullScreenDenied(true, "FullScreenDeniedLostWindow", this); |
michael@0 | 11072 | return; |
michael@0 | 11073 | } |
michael@0 | 11074 | if (nsContentUtils::IsFullscreenApiContentOnly() && |
michael@0 | 11075 | nsContentUtils::IsChromeDoc(this)) { |
michael@0 | 11076 | // Block fullscreen requests in the chrome document when the fullscreen API |
michael@0 | 11077 | // is configured for content only. |
michael@0 | 11078 | LogFullScreenDenied(true, "FullScreenDeniedContentOnly", this); |
michael@0 | 11079 | return; |
michael@0 | 11080 | } |
michael@0 | 11081 | if (!IsFullScreenEnabled(aWasCallerChrome, true)) { |
michael@0 | 11082 | // IsFullScreenEnabled calls LogFullScreenDenied, no need to log. |
michael@0 | 11083 | return; |
michael@0 | 11084 | } |
michael@0 | 11085 | if (GetFullScreenElement() && |
michael@0 | 11086 | !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) { |
michael@0 | 11087 | // If this document is full-screen, only grant full-screen requests from |
michael@0 | 11088 | // a descendant of the current full-screen element. |
michael@0 | 11089 | LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this); |
michael@0 | 11090 | return; |
michael@0 | 11091 | } |
michael@0 | 11092 | if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) { |
michael@0 | 11093 | LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this); |
michael@0 | 11094 | return; |
michael@0 | 11095 | } |
michael@0 | 11096 | // Deny requests when a windowed plugin is focused. |
michael@0 | 11097 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 11098 | if (!fm) { |
michael@0 | 11099 | NS_WARNING("Failed to retrieve focus manager in full-screen request."); |
michael@0 | 11100 | return; |
michael@0 | 11101 | } |
michael@0 | 11102 | nsCOMPtr<nsIDOMElement> focusedElement; |
michael@0 | 11103 | fm->GetFocusedElement(getter_AddRefs(focusedElement)); |
michael@0 | 11104 | if (focusedElement) { |
michael@0 | 11105 | nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement); |
michael@0 | 11106 | if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) { |
michael@0 | 11107 | LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this); |
michael@0 | 11108 | return; |
michael@0 | 11109 | } |
michael@0 | 11110 | } |
michael@0 | 11111 | |
michael@0 | 11112 | // Stash a reference to any existing fullscreen doc, we'll use this later |
michael@0 | 11113 | // to detect if the origin which is fullscreen has changed. |
michael@0 | 11114 | nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this); |
michael@0 | 11115 | |
michael@0 | 11116 | AddFullscreenApprovedObserver(); |
michael@0 | 11117 | |
michael@0 | 11118 | // Stores a list of documents which we must dispatch "mozfullscreenchange" |
michael@0 | 11119 | // too. We're required by the spec to dispatch the events in root-to-leaf |
michael@0 | 11120 | // order, but we traverse the doctree in a leaf-to-root order, so we save |
michael@0 | 11121 | // references to the documents we must dispatch to so that we get the order |
michael@0 | 11122 | // as specified. |
michael@0 | 11123 | nsAutoTArray<nsIDocument*, 8> changed; |
michael@0 | 11124 | |
michael@0 | 11125 | // Remember the root document, so that if a full-screen document is hidden |
michael@0 | 11126 | // we can reset full-screen state in the remaining visible full-screen documents. |
michael@0 | 11127 | nsIDocument* fullScreenRootDoc = GetFullscreenRootDocument(this); |
michael@0 | 11128 | if (fullScreenRootDoc->IsFullScreenDoc()) { |
michael@0 | 11129 | // A document is already in fullscreen, unlock the mouse pointer |
michael@0 | 11130 | // before setting a new document to fullscreen |
michael@0 | 11131 | UnlockPointer(); |
michael@0 | 11132 | } |
michael@0 | 11133 | |
michael@0 | 11134 | // If a document is already in fullscreen, then unlock the mouse pointer |
michael@0 | 11135 | // before setting a new document to fullscreen |
michael@0 | 11136 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 11137 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 11138 | if (pointerLockedElement) { |
michael@0 | 11139 | UnlockPointer(); |
michael@0 | 11140 | } |
michael@0 | 11141 | |
michael@0 | 11142 | // Set the full-screen element. This sets the full-screen style on the |
michael@0 | 11143 | // element, and the full-screen-ancestor styles on ancestors of the element |
michael@0 | 11144 | // in this document. |
michael@0 | 11145 | DebugOnly<bool> x = FullScreenStackPush(aElement); |
michael@0 | 11146 | NS_ASSERTION(x, "Full-screen state of requesting doc should always change!"); |
michael@0 | 11147 | changed.AppendElement(this); |
michael@0 | 11148 | |
michael@0 | 11149 | // Propagate up the document hierarchy, setting the full-screen element as |
michael@0 | 11150 | // the element's container in ancestor documents. This also sets the |
michael@0 | 11151 | // appropriate css styles as well. Note we don't propagate down the |
michael@0 | 11152 | // document hierarchy, the full-screen element (or its container) is not |
michael@0 | 11153 | // visible there. Stop when we reach the root document. |
michael@0 | 11154 | nsIDocument* child = this; |
michael@0 | 11155 | while (true) { |
michael@0 | 11156 | child->SetFullscreenRoot(fullScreenRootDoc); |
michael@0 | 11157 | NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc, |
michael@0 | 11158 | "Fullscreen root should be set!"); |
michael@0 | 11159 | if (child == fullScreenRootDoc) { |
michael@0 | 11160 | break; |
michael@0 | 11161 | } |
michael@0 | 11162 | nsIDocument* parent = child->GetParentDocument(); |
michael@0 | 11163 | Element* element = parent->FindContentForSubDocument(child)->AsElement(); |
michael@0 | 11164 | if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) { |
michael@0 | 11165 | changed.AppendElement(parent); |
michael@0 | 11166 | child = parent; |
michael@0 | 11167 | } else { |
michael@0 | 11168 | // We've reached either the root, or a point in the doctree where the |
michael@0 | 11169 | // new full-screen element container is the same as the previous |
michael@0 | 11170 | // full-screen element's container. No more changes need to be made |
michael@0 | 11171 | // to the full-screen stacks of documents further up the tree. |
michael@0 | 11172 | break; |
michael@0 | 11173 | } |
michael@0 | 11174 | } |
michael@0 | 11175 | |
michael@0 | 11176 | // Dispatch "mozfullscreenchange" events. Note this loop is in reverse |
michael@0 | 11177 | // order so that the events for the root document arrives before the leaf |
michael@0 | 11178 | // document, as required by the spec. |
michael@0 | 11179 | for (uint32_t i = 0; i < changed.Length(); ++i) { |
michael@0 | 11180 | DispatchFullScreenChange(changed[changed.Length() - i - 1]); |
michael@0 | 11181 | } |
michael@0 | 11182 | |
michael@0 | 11183 | // If this document hasn't already been approved in this session, |
michael@0 | 11184 | // check to see if the user has granted the fullscreen access |
michael@0 | 11185 | // to the document's principal's host, if it has one. Note that documents |
michael@0 | 11186 | // in web apps which are the same origin as the web app are considered |
michael@0 | 11187 | // trusted and so are automatically approved. |
michael@0 | 11188 | if (!mIsApprovedForFullscreen) { |
michael@0 | 11189 | mIsApprovedForFullscreen = |
michael@0 | 11190 | !Preferences::GetBool("full-screen-api.approval-required") || |
michael@0 | 11191 | NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED || |
michael@0 | 11192 | nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen"); |
michael@0 | 11193 | } |
michael@0 | 11194 | |
michael@0 | 11195 | // If this document, or a document with the same principal has not |
michael@0 | 11196 | // already been approved for fullscreen this fullscreen-session, dispatch |
michael@0 | 11197 | // an event so that chrome knows to pop up a warning/approval UI. |
michael@0 | 11198 | // Note previousFullscreenDoc=nullptr upon first entry, so we always |
michael@0 | 11199 | // take this path on the first time we enter fullscreen in a fullscreen |
michael@0 | 11200 | // session. |
michael@0 | 11201 | if (!mIsApprovedForFullscreen || |
michael@0 | 11202 | !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) { |
michael@0 | 11203 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 11204 | new AsyncEventDispatcher(this, |
michael@0 | 11205 | NS_LITERAL_STRING("MozEnteredDomFullscreen"), |
michael@0 | 11206 | true, |
michael@0 | 11207 | true); |
michael@0 | 11208 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 11209 | } |
michael@0 | 11210 | |
michael@0 | 11211 | #ifdef DEBUG |
michael@0 | 11212 | // Note assertions must run before SetWindowFullScreen() as that does |
michael@0 | 11213 | // synchronous event dispatch which can run script which exits full-screen! |
michael@0 | 11214 | NS_ASSERTION(GetFullScreenElement() == aElement, |
michael@0 | 11215 | "Full-screen element should be the requested element!"); |
michael@0 | 11216 | NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc"); |
michael@0 | 11217 | nsCOMPtr<nsIDOMElement> fse; |
michael@0 | 11218 | GetMozFullScreenElement(getter_AddRefs(fse)); |
michael@0 | 11219 | nsCOMPtr<nsIContent> c(do_QueryInterface(fse)); |
michael@0 | 11220 | NS_ASSERTION(c->AsElement() == aElement, |
michael@0 | 11221 | "GetMozFullScreenElement should match GetFullScreenElement()"); |
michael@0 | 11222 | #endif |
michael@0 | 11223 | |
michael@0 | 11224 | // The origin which is fullscreen changed, send a notifiction so that the |
michael@0 | 11225 | // root document knows the origin of the document which requested fullscreen. |
michael@0 | 11226 | // This is used for the fullscreen approval UI. If we're in a child |
michael@0 | 11227 | // process, the root BrowserElementChild listens for this notification, |
michael@0 | 11228 | // and forwards it across to its BrowserElementParent, which |
michael@0 | 11229 | // re-broadcasts the message for the root document in its process. |
michael@0 | 11230 | if (aNotifyOnOriginChange && |
michael@0 | 11231 | !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) { |
michael@0 | 11232 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 11233 | nsIDocument* root = GetFullscreenRootDocument(this); |
michael@0 | 11234 | nsAutoString origin; |
michael@0 | 11235 | nsContentUtils::GetUTFOrigin(NodePrincipal(), origin); |
michael@0 | 11236 | os->NotifyObservers(root, "fullscreen-origin-change", origin.get()); |
michael@0 | 11237 | } |
michael@0 | 11238 | |
michael@0 | 11239 | // Make the window full-screen. Note we must make the state changes above |
michael@0 | 11240 | // before making the window full-screen, as then the document reports as |
michael@0 | 11241 | // being in full-screen mode when the chrome "fullscreen" event fires, |
michael@0 | 11242 | // enabling chrome to distinguish between browser and dom full-screen |
michael@0 | 11243 | // modes. Also note that nsGlobalWindow::SetFullScreen() (which |
michael@0 | 11244 | // SetWindowFullScreen() calls) proxies to the root window in its hierarchy, |
michael@0 | 11245 | // and does not operate on the a per-nsIDOMWindow basis. |
michael@0 | 11246 | SetWindowFullScreen(this, true); |
michael@0 | 11247 | } |
michael@0 | 11248 | |
michael@0 | 11249 | NS_IMETHODIMP |
michael@0 | 11250 | nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement) |
michael@0 | 11251 | { |
michael@0 | 11252 | ErrorResult rv; |
michael@0 | 11253 | Element* el = GetMozFullScreenElement(rv); |
michael@0 | 11254 | if (rv.Failed()) { |
michael@0 | 11255 | return rv.ErrorCode(); |
michael@0 | 11256 | } |
michael@0 | 11257 | nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el); |
michael@0 | 11258 | retval.forget(aFullScreenElement); |
michael@0 | 11259 | return NS_OK; |
michael@0 | 11260 | } |
michael@0 | 11261 | |
michael@0 | 11262 | Element* |
michael@0 | 11263 | nsDocument::GetMozFullScreenElement(ErrorResult& rv) |
michael@0 | 11264 | { |
michael@0 | 11265 | if (IsFullScreenDoc()) { |
michael@0 | 11266 | // Must have a full-screen element while in full-screen mode. |
michael@0 | 11267 | Element* el = GetFullScreenElement(); |
michael@0 | 11268 | if (!el) { |
michael@0 | 11269 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 11270 | } |
michael@0 | 11271 | return el; |
michael@0 | 11272 | } |
michael@0 | 11273 | return nullptr; |
michael@0 | 11274 | } |
michael@0 | 11275 | |
michael@0 | 11276 | Element* |
michael@0 | 11277 | nsDocument::GetFullScreenElement() |
michael@0 | 11278 | { |
michael@0 | 11279 | Element* element = FullScreenStackTop(); |
michael@0 | 11280 | NS_ASSERTION(!element || |
michael@0 | 11281 | element->IsFullScreenAncestor(), |
michael@0 | 11282 | "Fullscreen element should have fullscreen styles applied"); |
michael@0 | 11283 | return element; |
michael@0 | 11284 | } |
michael@0 | 11285 | |
michael@0 | 11286 | NS_IMETHODIMP |
michael@0 | 11287 | nsDocument::GetMozFullScreen(bool *aFullScreen) |
michael@0 | 11288 | { |
michael@0 | 11289 | *aFullScreen = MozFullScreen(); |
michael@0 | 11290 | return NS_OK; |
michael@0 | 11291 | } |
michael@0 | 11292 | |
michael@0 | 11293 | NS_IMETHODIMP |
michael@0 | 11294 | nsDocument::GetMozFullScreenEnabled(bool *aFullScreen) |
michael@0 | 11295 | { |
michael@0 | 11296 | NS_ENSURE_ARG_POINTER(aFullScreen); |
michael@0 | 11297 | *aFullScreen = MozFullScreenEnabled(); |
michael@0 | 11298 | return NS_OK; |
michael@0 | 11299 | } |
michael@0 | 11300 | |
michael@0 | 11301 | bool |
michael@0 | 11302 | nsDocument::MozFullScreenEnabled() |
michael@0 | 11303 | { |
michael@0 | 11304 | return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false); |
michael@0 | 11305 | } |
michael@0 | 11306 | |
michael@0 | 11307 | static bool |
michael@0 | 11308 | HasFullScreenSubDocument(nsIDocument* aDoc) |
michael@0 | 11309 | { |
michael@0 | 11310 | uint32_t count = CountFullscreenSubDocuments(aDoc); |
michael@0 | 11311 | NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!"); |
michael@0 | 11312 | return count >= 1; |
michael@0 | 11313 | } |
michael@0 | 11314 | |
michael@0 | 11315 | bool |
michael@0 | 11316 | nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure) |
michael@0 | 11317 | { |
michael@0 | 11318 | if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) { |
michael@0 | 11319 | // Chrome code can always use the full-screen API, provided it's not |
michael@0 | 11320 | // explicitly disabled. Note IsCallerChrome() returns true when running |
michael@0 | 11321 | // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an |
michael@0 | 11322 | // nsRunnable! |
michael@0 | 11323 | return true; |
michael@0 | 11324 | } |
michael@0 | 11325 | |
michael@0 | 11326 | if (!nsContentUtils::IsFullScreenApiEnabled()) { |
michael@0 | 11327 | LogFullScreenDenied(aLogFailure, "FullScreenDeniedDisabled", this); |
michael@0 | 11328 | return false; |
michael@0 | 11329 | } |
michael@0 | 11330 | if (!IsVisible()) { |
michael@0 | 11331 | LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this); |
michael@0 | 11332 | return false; |
michael@0 | 11333 | } |
michael@0 | 11334 | if (HasFullScreenSubDocument(this)) { |
michael@0 | 11335 | LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this); |
michael@0 | 11336 | return false; |
michael@0 | 11337 | } |
michael@0 | 11338 | |
michael@0 | 11339 | // Ensure that all ancestor <iframe> elements have the allowfullscreen |
michael@0 | 11340 | // boolean attribute set. |
michael@0 | 11341 | nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); |
michael@0 | 11342 | bool allowed = false; |
michael@0 | 11343 | if (docShell) { |
michael@0 | 11344 | docShell->GetFullscreenAllowed(&allowed); |
michael@0 | 11345 | } |
michael@0 | 11346 | if (!allowed) { |
michael@0 | 11347 | LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this); |
michael@0 | 11348 | } |
michael@0 | 11349 | |
michael@0 | 11350 | return allowed; |
michael@0 | 11351 | } |
michael@0 | 11352 | |
michael@0 | 11353 | static void |
michael@0 | 11354 | DispatchPointerLockChange(nsIDocument* aTarget) |
michael@0 | 11355 | { |
michael@0 | 11356 | if (!aTarget) { |
michael@0 | 11357 | return; |
michael@0 | 11358 | } |
michael@0 | 11359 | |
michael@0 | 11360 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 11361 | new AsyncEventDispatcher(aTarget, |
michael@0 | 11362 | NS_LITERAL_STRING("mozpointerlockchange"), |
michael@0 | 11363 | true, |
michael@0 | 11364 | false); |
michael@0 | 11365 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 11366 | } |
michael@0 | 11367 | |
michael@0 | 11368 | static void |
michael@0 | 11369 | DispatchPointerLockError(nsIDocument* aTarget) |
michael@0 | 11370 | { |
michael@0 | 11371 | if (!aTarget) { |
michael@0 | 11372 | return; |
michael@0 | 11373 | } |
michael@0 | 11374 | |
michael@0 | 11375 | nsRefPtr<AsyncEventDispatcher> asyncDispatcher = |
michael@0 | 11376 | new AsyncEventDispatcher(aTarget, |
michael@0 | 11377 | NS_LITERAL_STRING("mozpointerlockerror"), |
michael@0 | 11378 | true, |
michael@0 | 11379 | false); |
michael@0 | 11380 | asyncDispatcher->PostDOMEvent(); |
michael@0 | 11381 | } |
michael@0 | 11382 | |
michael@0 | 11383 | mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest; |
michael@0 | 11384 | |
michael@0 | 11385 | class nsPointerLockPermissionRequest : public nsRunnable, |
michael@0 | 11386 | public nsIContentPermissionRequest |
michael@0 | 11387 | { |
michael@0 | 11388 | public: |
michael@0 | 11389 | nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller) |
michael@0 | 11390 | : mElement(do_GetWeakReference(aElement)), |
michael@0 | 11391 | mDocument(do_GetWeakReference(aElement->OwnerDoc())), |
michael@0 | 11392 | mUserInputOrChromeCaller(aUserInputOrChromeCaller) {} |
michael@0 | 11393 | |
michael@0 | 11394 | virtual ~nsPointerLockPermissionRequest() {} |
michael@0 | 11395 | |
michael@0 | 11396 | NS_DECL_ISUPPORTS |
michael@0 | 11397 | NS_DECL_NSICONTENTPERMISSIONREQUEST |
michael@0 | 11398 | |
michael@0 | 11399 | NS_IMETHOD Run() |
michael@0 | 11400 | { |
michael@0 | 11401 | nsCOMPtr<Element> e = do_QueryReferent(mElement); |
michael@0 | 11402 | nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
michael@0 | 11403 | if (!e || !d || gPendingPointerLockRequest != this || |
michael@0 | 11404 | e->GetCurrentDoc() != d) { |
michael@0 | 11405 | Handled(); |
michael@0 | 11406 | DispatchPointerLockError(d); |
michael@0 | 11407 | return NS_OK; |
michael@0 | 11408 | } |
michael@0 | 11409 | |
michael@0 | 11410 | // We're about to enter fullscreen mode. |
michael@0 | 11411 | nsDocument* doc = static_cast<nsDocument*>(d.get()); |
michael@0 | 11412 | if (doc->mAsyncFullscreenPending || |
michael@0 | 11413 | (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) { |
michael@0 | 11414 | // We're still waiting for approval. |
michael@0 | 11415 | return NS_OK; |
michael@0 | 11416 | } |
michael@0 | 11417 | |
michael@0 | 11418 | if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) { |
michael@0 | 11419 | Allow(JS::UndefinedHandleValue); |
michael@0 | 11420 | return NS_OK; |
michael@0 | 11421 | } |
michael@0 | 11422 | |
michael@0 | 11423 | // In non-fullscreen mode user input (or chrome caller) is required! |
michael@0 | 11424 | // Also, don't let the page to try to get the permission too many times. |
michael@0 | 11425 | if (!mUserInputOrChromeCaller || |
michael@0 | 11426 | doc->mCancelledPointerLockRequests > 2) { |
michael@0 | 11427 | Handled(); |
michael@0 | 11428 | DispatchPointerLockError(d); |
michael@0 | 11429 | return NS_OK; |
michael@0 | 11430 | } |
michael@0 | 11431 | |
michael@0 | 11432 | // Handling a request from user input in non-fullscreen mode. |
michael@0 | 11433 | // Do a normal permission check. |
michael@0 | 11434 | nsCOMPtr<nsIContentPermissionPrompt> prompt = |
michael@0 | 11435 | do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); |
michael@0 | 11436 | if (prompt) { |
michael@0 | 11437 | prompt->Prompt(this); |
michael@0 | 11438 | } |
michael@0 | 11439 | |
michael@0 | 11440 | return NS_OK; |
michael@0 | 11441 | } |
michael@0 | 11442 | |
michael@0 | 11443 | void Handled() |
michael@0 | 11444 | { |
michael@0 | 11445 | mElement = nullptr; |
michael@0 | 11446 | mDocument = nullptr; |
michael@0 | 11447 | if (gPendingPointerLockRequest == this) { |
michael@0 | 11448 | gPendingPointerLockRequest = nullptr; |
michael@0 | 11449 | } |
michael@0 | 11450 | } |
michael@0 | 11451 | |
michael@0 | 11452 | nsWeakPtr mElement; |
michael@0 | 11453 | nsWeakPtr mDocument; |
michael@0 | 11454 | bool mUserInputOrChromeCaller; |
michael@0 | 11455 | }; |
michael@0 | 11456 | |
michael@0 | 11457 | NS_IMPL_ISUPPORTS_INHERITED(nsPointerLockPermissionRequest, |
michael@0 | 11458 | nsRunnable, |
michael@0 | 11459 | nsIContentPermissionRequest) |
michael@0 | 11460 | |
michael@0 | 11461 | NS_IMETHODIMP |
michael@0 | 11462 | nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes) |
michael@0 | 11463 | { |
michael@0 | 11464 | nsTArray<nsString> emptyOptions; |
michael@0 | 11465 | return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"), |
michael@0 | 11466 | NS_LITERAL_CSTRING("unused"), |
michael@0 | 11467 | emptyOptions, |
michael@0 | 11468 | aTypes); |
michael@0 | 11469 | } |
michael@0 | 11470 | |
michael@0 | 11471 | NS_IMETHODIMP |
michael@0 | 11472 | nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal) |
michael@0 | 11473 | { |
michael@0 | 11474 | nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
michael@0 | 11475 | if (d) { |
michael@0 | 11476 | NS_ADDREF(*aPrincipal = d->NodePrincipal()); |
michael@0 | 11477 | } |
michael@0 | 11478 | return NS_OK; |
michael@0 | 11479 | } |
michael@0 | 11480 | |
michael@0 | 11481 | NS_IMETHODIMP |
michael@0 | 11482 | nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow) |
michael@0 | 11483 | { |
michael@0 | 11484 | nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
michael@0 | 11485 | if (d) { |
michael@0 | 11486 | NS_IF_ADDREF(*aWindow = d->GetInnerWindow()); |
michael@0 | 11487 | } |
michael@0 | 11488 | return NS_OK; |
michael@0 | 11489 | } |
michael@0 | 11490 | |
michael@0 | 11491 | NS_IMETHODIMP |
michael@0 | 11492 | nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement) |
michael@0 | 11493 | { |
michael@0 | 11494 | // It is enough to implement GetWindow. |
michael@0 | 11495 | *aElement = nullptr; |
michael@0 | 11496 | return NS_OK; |
michael@0 | 11497 | } |
michael@0 | 11498 | |
michael@0 | 11499 | NS_IMETHODIMP |
michael@0 | 11500 | nsPointerLockPermissionRequest::Cancel() |
michael@0 | 11501 | { |
michael@0 | 11502 | nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); |
michael@0 | 11503 | Handled(); |
michael@0 | 11504 | if (d) { |
michael@0 | 11505 | static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++; |
michael@0 | 11506 | DispatchPointerLockError(d); |
michael@0 | 11507 | } |
michael@0 | 11508 | return NS_OK; |
michael@0 | 11509 | } |
michael@0 | 11510 | |
michael@0 | 11511 | NS_IMETHODIMP |
michael@0 | 11512 | nsPointerLockPermissionRequest::Allow(JS::HandleValue aChoices) |
michael@0 | 11513 | { |
michael@0 | 11514 | MOZ_ASSERT(aChoices.isUndefined()); |
michael@0 | 11515 | |
michael@0 | 11516 | nsCOMPtr<Element> e = do_QueryReferent(mElement); |
michael@0 | 11517 | nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); |
michael@0 | 11518 | nsDocument* d = static_cast<nsDocument*>(doc.get()); |
michael@0 | 11519 | if (!e || !d || gPendingPointerLockRequest != this || |
michael@0 | 11520 | e->GetCurrentDoc() != d || |
michael@0 | 11521 | (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) { |
michael@0 | 11522 | Handled(); |
michael@0 | 11523 | DispatchPointerLockError(d); |
michael@0 | 11524 | return NS_OK; |
michael@0 | 11525 | } |
michael@0 | 11526 | |
michael@0 | 11527 | // Mark handled here so that we don't need to call it everywhere below. |
michael@0 | 11528 | Handled(); |
michael@0 | 11529 | |
michael@0 | 11530 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 11531 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 11532 | if (e == pointerLockedElement) { |
michael@0 | 11533 | DispatchPointerLockChange(d); |
michael@0 | 11534 | return NS_OK; |
michael@0 | 11535 | } |
michael@0 | 11536 | |
michael@0 | 11537 | // Note, we must bypass focus change, so pass true as the last parameter! |
michael@0 | 11538 | if (!d->ShouldLockPointer(e, pointerLockedElement, true)) { |
michael@0 | 11539 | DispatchPointerLockError(d); |
michael@0 | 11540 | return NS_OK; |
michael@0 | 11541 | } |
michael@0 | 11542 | |
michael@0 | 11543 | if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) { |
michael@0 | 11544 | DispatchPointerLockError(d); |
michael@0 | 11545 | return NS_OK; |
michael@0 | 11546 | } |
michael@0 | 11547 | |
michael@0 | 11548 | d->mCancelledPointerLockRequests = 0; |
michael@0 | 11549 | e->SetPointerLock(); |
michael@0 | 11550 | EventStateManager::sPointerLockedElement = do_GetWeakReference(e); |
michael@0 | 11551 | EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc); |
michael@0 | 11552 | NS_ASSERTION(EventStateManager::sPointerLockedElement && |
michael@0 | 11553 | EventStateManager::sPointerLockedDoc, |
michael@0 | 11554 | "aElement and this should support weak references!"); |
michael@0 | 11555 | |
michael@0 | 11556 | DispatchPointerLockChange(d); |
michael@0 | 11557 | return NS_OK; |
michael@0 | 11558 | } |
michael@0 | 11559 | |
michael@0 | 11560 | void |
michael@0 | 11561 | nsDocument::SetApprovedForFullscreen(bool aIsApproved) |
michael@0 | 11562 | { |
michael@0 | 11563 | mIsApprovedForFullscreen = aIsApproved; |
michael@0 | 11564 | } |
michael@0 | 11565 | |
michael@0 | 11566 | nsresult |
michael@0 | 11567 | nsDocument::Observe(nsISupports *aSubject, |
michael@0 | 11568 | const char *aTopic, |
michael@0 | 11569 | const char16_t *aData) |
michael@0 | 11570 | { |
michael@0 | 11571 | if (strcmp("fullscreen-approved", aTopic) == 0) { |
michael@0 | 11572 | nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject)); |
michael@0 | 11573 | if (subject != this) { |
michael@0 | 11574 | return NS_OK; |
michael@0 | 11575 | } |
michael@0 | 11576 | SetApprovedForFullscreen(true); |
michael@0 | 11577 | if (gPendingPointerLockRequest) { |
michael@0 | 11578 | // We have a request pending. Create a clone of it and re-dispatch so that |
michael@0 | 11579 | // Run() method gets called again. |
michael@0 | 11580 | nsCOMPtr<Element> el = |
michael@0 | 11581 | do_QueryReferent(gPendingPointerLockRequest->mElement); |
michael@0 | 11582 | nsCOMPtr<nsIDocument> doc = |
michael@0 | 11583 | do_QueryReferent(gPendingPointerLockRequest->mDocument); |
michael@0 | 11584 | bool userInputOrChromeCaller = |
michael@0 | 11585 | gPendingPointerLockRequest->mUserInputOrChromeCaller; |
michael@0 | 11586 | gPendingPointerLockRequest->Handled(); |
michael@0 | 11587 | if (doc == this && el && el->GetCurrentDoc() == doc) { |
michael@0 | 11588 | nsPointerLockPermissionRequest* clone = |
michael@0 | 11589 | new nsPointerLockPermissionRequest(el, userInputOrChromeCaller); |
michael@0 | 11590 | gPendingPointerLockRequest = clone; |
michael@0 | 11591 | nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get(); |
michael@0 | 11592 | NS_DispatchToMainThread(r); |
michael@0 | 11593 | } |
michael@0 | 11594 | } |
michael@0 | 11595 | } |
michael@0 | 11596 | return NS_OK; |
michael@0 | 11597 | } |
michael@0 | 11598 | |
michael@0 | 11599 | void |
michael@0 | 11600 | nsDocument::RequestPointerLock(Element* aElement) |
michael@0 | 11601 | { |
michael@0 | 11602 | NS_ASSERTION(aElement, |
michael@0 | 11603 | "Must pass non-null element to nsDocument::RequestPointerLock"); |
michael@0 | 11604 | |
michael@0 | 11605 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 11606 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 11607 | if (aElement == pointerLockedElement) { |
michael@0 | 11608 | DispatchPointerLockChange(this); |
michael@0 | 11609 | return; |
michael@0 | 11610 | } |
michael@0 | 11611 | |
michael@0 | 11612 | if (!ShouldLockPointer(aElement, pointerLockedElement)) { |
michael@0 | 11613 | DispatchPointerLockError(this); |
michael@0 | 11614 | return; |
michael@0 | 11615 | } |
michael@0 | 11616 | |
michael@0 | 11617 | bool userInputOrChromeCaller = EventStateManager::IsHandlingUserInput() || |
michael@0 | 11618 | nsContentUtils::IsCallerChrome(); |
michael@0 | 11619 | |
michael@0 | 11620 | gPendingPointerLockRequest = |
michael@0 | 11621 | new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller); |
michael@0 | 11622 | nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get(); |
michael@0 | 11623 | NS_DispatchToMainThread(r); |
michael@0 | 11624 | } |
michael@0 | 11625 | |
michael@0 | 11626 | bool |
michael@0 | 11627 | nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock, |
michael@0 | 11628 | bool aNoFocusCheck) |
michael@0 | 11629 | { |
michael@0 | 11630 | // Check if pointer lock pref is enabled |
michael@0 | 11631 | if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) { |
michael@0 | 11632 | NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled"); |
michael@0 | 11633 | return false; |
michael@0 | 11634 | } |
michael@0 | 11635 | |
michael@0 | 11636 | if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) { |
michael@0 | 11637 | NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document"); |
michael@0 | 11638 | return false; |
michael@0 | 11639 | } |
michael@0 | 11640 | |
michael@0 | 11641 | if (!aElement->IsInDoc()) { |
michael@0 | 11642 | NS_WARNING("ShouldLockPointer(): Element without Document"); |
michael@0 | 11643 | return false; |
michael@0 | 11644 | } |
michael@0 | 11645 | |
michael@0 | 11646 | if (mSandboxFlags & SANDBOXED_POINTER_LOCK) { |
michael@0 | 11647 | NS_WARNING("ShouldLockPointer(): Document is sandboxed and doesn't allow pointer-lock"); |
michael@0 | 11648 | return false; |
michael@0 | 11649 | } |
michael@0 | 11650 | |
michael@0 | 11651 | // Check if the element is in a document with a docshell. |
michael@0 | 11652 | nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc(); |
michael@0 | 11653 | if (!ownerDoc->GetContainer()) { |
michael@0 | 11654 | return false; |
michael@0 | 11655 | } |
michael@0 | 11656 | nsCOMPtr<nsPIDOMWindow> ownerWindow = ownerDoc->GetWindow(); |
michael@0 | 11657 | if (!ownerWindow) { |
michael@0 | 11658 | return false; |
michael@0 | 11659 | } |
michael@0 | 11660 | nsCOMPtr<nsPIDOMWindow> ownerInnerWindow = ownerDoc->GetInnerWindow(); |
michael@0 | 11661 | if (!ownerInnerWindow) { |
michael@0 | 11662 | return false; |
michael@0 | 11663 | } |
michael@0 | 11664 | if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) { |
michael@0 | 11665 | return false; |
michael@0 | 11666 | } |
michael@0 | 11667 | |
michael@0 | 11668 | nsCOMPtr<nsIDOMWindow> top; |
michael@0 | 11669 | ownerWindow->GetScriptableTop(getter_AddRefs(top)); |
michael@0 | 11670 | nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top); |
michael@0 | 11671 | if (!piTop || !piTop->GetExtantDoc() || |
michael@0 | 11672 | piTop->GetExtantDoc()->Hidden()) { |
michael@0 | 11673 | NS_WARNING("ShouldLockPointer(): Top document isn't visible."); |
michael@0 | 11674 | return false; |
michael@0 | 11675 | } |
michael@0 | 11676 | |
michael@0 | 11677 | if (!aNoFocusCheck) { |
michael@0 | 11678 | mozilla::ErrorResult rv; |
michael@0 | 11679 | if (!piTop->GetExtantDoc()->HasFocus(rv)) { |
michael@0 | 11680 | NS_WARNING("ShouldLockPointer(): Top document isn't focused."); |
michael@0 | 11681 | return false; |
michael@0 | 11682 | } |
michael@0 | 11683 | } |
michael@0 | 11684 | |
michael@0 | 11685 | return true; |
michael@0 | 11686 | } |
michael@0 | 11687 | |
michael@0 | 11688 | bool |
michael@0 | 11689 | nsDocument::SetPointerLock(Element* aElement, int aCursorStyle) |
michael@0 | 11690 | { |
michael@0 | 11691 | // NOTE: aElement will be nullptr when unlocking. |
michael@0 | 11692 | nsCOMPtr<nsPIDOMWindow> window = GetWindow(); |
michael@0 | 11693 | if (!window) { |
michael@0 | 11694 | NS_WARNING("SetPointerLock(): No Window"); |
michael@0 | 11695 | return false; |
michael@0 | 11696 | } |
michael@0 | 11697 | |
michael@0 | 11698 | nsIDocShell *docShell = window->GetDocShell(); |
michael@0 | 11699 | if (!docShell) { |
michael@0 | 11700 | NS_WARNING("SetPointerLock(): No DocShell (window already closed?)"); |
michael@0 | 11701 | return false; |
michael@0 | 11702 | } |
michael@0 | 11703 | |
michael@0 | 11704 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 11705 | docShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 11706 | if (!presContext) { |
michael@0 | 11707 | NS_WARNING("SetPointerLock(): Unable to get presContext in \ |
michael@0 | 11708 | domWindow->GetDocShell()->GetPresContext()"); |
michael@0 | 11709 | return false; |
michael@0 | 11710 | } |
michael@0 | 11711 | |
michael@0 | 11712 | nsCOMPtr<nsIPresShell> shell = presContext->PresShell(); |
michael@0 | 11713 | if (!shell) { |
michael@0 | 11714 | NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()"); |
michael@0 | 11715 | return false; |
michael@0 | 11716 | } |
michael@0 | 11717 | |
michael@0 | 11718 | nsIFrame* rootFrame = shell->GetRootFrame(); |
michael@0 | 11719 | if (!rootFrame) { |
michael@0 | 11720 | NS_WARNING("SetPointerLock(): Unable to get root frame"); |
michael@0 | 11721 | return false; |
michael@0 | 11722 | } |
michael@0 | 11723 | |
michael@0 | 11724 | nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget(); |
michael@0 | 11725 | if (!widget) { |
michael@0 | 11726 | NS_WARNING("SetPointerLock(): Unable to find widget in \ |
michael@0 | 11727 | shell->GetRootFrame()->GetNearestWidget();"); |
michael@0 | 11728 | return false; |
michael@0 | 11729 | } |
michael@0 | 11730 | |
michael@0 | 11731 | if (aElement && (aElement->OwnerDoc() != this)) { |
michael@0 | 11732 | NS_WARNING("SetPointerLock(): Element not in this document."); |
michael@0 | 11733 | return false; |
michael@0 | 11734 | } |
michael@0 | 11735 | |
michael@0 | 11736 | // Hide the cursor and set pointer lock for future mouse events |
michael@0 | 11737 | nsRefPtr<EventStateManager> esm = presContext->EventStateManager(); |
michael@0 | 11738 | esm->SetCursor(aCursorStyle, nullptr, false, |
michael@0 | 11739 | 0.0f, 0.0f, widget, true); |
michael@0 | 11740 | esm->SetPointerLock(widget, aElement); |
michael@0 | 11741 | |
michael@0 | 11742 | return true; |
michael@0 | 11743 | } |
michael@0 | 11744 | |
michael@0 | 11745 | void |
michael@0 | 11746 | nsDocument::UnlockPointer(nsIDocument* aDoc) |
michael@0 | 11747 | { |
michael@0 | 11748 | if (!EventStateManager::sIsPointerLocked) { |
michael@0 | 11749 | return; |
michael@0 | 11750 | } |
michael@0 | 11751 | |
michael@0 | 11752 | nsCOMPtr<nsIDocument> pointerLockedDoc = |
michael@0 | 11753 | do_QueryReferent(EventStateManager::sPointerLockedDoc); |
michael@0 | 11754 | if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) { |
michael@0 | 11755 | return; |
michael@0 | 11756 | } |
michael@0 | 11757 | nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get()); |
michael@0 | 11758 | if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) { |
michael@0 | 11759 | return; |
michael@0 | 11760 | } |
michael@0 | 11761 | |
michael@0 | 11762 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 11763 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 11764 | if (pointerLockedElement) { |
michael@0 | 11765 | pointerLockedElement->ClearPointerLock(); |
michael@0 | 11766 | } |
michael@0 | 11767 | |
michael@0 | 11768 | EventStateManager::sPointerLockedElement = nullptr; |
michael@0 | 11769 | EventStateManager::sPointerLockedDoc = nullptr; |
michael@0 | 11770 | static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc; |
michael@0 | 11771 | gPendingPointerLockRequest = nullptr; |
michael@0 | 11772 | DispatchPointerLockChange(pointerLockedDoc); |
michael@0 | 11773 | } |
michael@0 | 11774 | |
michael@0 | 11775 | void |
michael@0 | 11776 | nsIDocument::UnlockPointer(nsIDocument* aDoc) |
michael@0 | 11777 | { |
michael@0 | 11778 | nsDocument::UnlockPointer(aDoc); |
michael@0 | 11779 | } |
michael@0 | 11780 | |
michael@0 | 11781 | NS_IMETHODIMP |
michael@0 | 11782 | nsDocument::MozExitPointerLock() |
michael@0 | 11783 | { |
michael@0 | 11784 | nsIDocument::MozExitPointerLock(); |
michael@0 | 11785 | return NS_OK; |
michael@0 | 11786 | } |
michael@0 | 11787 | |
michael@0 | 11788 | NS_IMETHODIMP |
michael@0 | 11789 | nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement) |
michael@0 | 11790 | { |
michael@0 | 11791 | Element* el = nsIDocument::GetMozPointerLockElement(); |
michael@0 | 11792 | nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el); |
michael@0 | 11793 | retval.forget(aPointerLockedElement); |
michael@0 | 11794 | return NS_OK; |
michael@0 | 11795 | } |
michael@0 | 11796 | |
michael@0 | 11797 | Element* |
michael@0 | 11798 | nsIDocument::GetMozPointerLockElement() |
michael@0 | 11799 | { |
michael@0 | 11800 | nsCOMPtr<Element> pointerLockedElement = |
michael@0 | 11801 | do_QueryReferent(EventStateManager::sPointerLockedElement); |
michael@0 | 11802 | if (!pointerLockedElement) { |
michael@0 | 11803 | return nullptr; |
michael@0 | 11804 | } |
michael@0 | 11805 | |
michael@0 | 11806 | // Make sure pointer locked element is in the same document. |
michael@0 | 11807 | nsCOMPtr<nsIDocument> pointerLockedDoc = |
michael@0 | 11808 | do_QueryReferent(EventStateManager::sPointerLockedDoc); |
michael@0 | 11809 | if (pointerLockedDoc != this) { |
michael@0 | 11810 | return nullptr; |
michael@0 | 11811 | } |
michael@0 | 11812 | |
michael@0 | 11813 | return pointerLockedElement; |
michael@0 | 11814 | } |
michael@0 | 11815 | |
michael@0 | 11816 | void |
michael@0 | 11817 | nsDocument::XPCOMShutdown() |
michael@0 | 11818 | { |
michael@0 | 11819 | gPendingPointerLockRequest = nullptr; |
michael@0 | 11820 | sProcessingStack.destroyIfConstructed(); |
michael@0 | 11821 | } |
michael@0 | 11822 | |
michael@0 | 11823 | void |
michael@0 | 11824 | nsDocument::UpdateVisibilityState() |
michael@0 | 11825 | { |
michael@0 | 11826 | dom::VisibilityState oldState = mVisibilityState; |
michael@0 | 11827 | mVisibilityState = GetVisibilityState(); |
michael@0 | 11828 | if (oldState != mVisibilityState) { |
michael@0 | 11829 | nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
michael@0 | 11830 | NS_LITERAL_STRING("visibilitychange"), |
michael@0 | 11831 | /* bubbles = */ true, |
michael@0 | 11832 | /* cancelable = */ false); |
michael@0 | 11833 | nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this), |
michael@0 | 11834 | NS_LITERAL_STRING("mozvisibilitychange"), |
michael@0 | 11835 | /* bubbles = */ true, |
michael@0 | 11836 | /* cancelable = */ false); |
michael@0 | 11837 | |
michael@0 | 11838 | EnumerateFreezableElements(NotifyActivityChanged, nullptr); |
michael@0 | 11839 | } |
michael@0 | 11840 | } |
michael@0 | 11841 | |
michael@0 | 11842 | VisibilityState |
michael@0 | 11843 | nsDocument::GetVisibilityState() const |
michael@0 | 11844 | { |
michael@0 | 11845 | // We have to check a few pieces of information here: |
michael@0 | 11846 | // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters. |
michael@0 | 11847 | // 2) Do we have an outer window? If not, we're hidden. Note that we don't |
michael@0 | 11848 | // want to use GetWindow here because it does weird groveling for windows |
michael@0 | 11849 | // in some cases. |
michael@0 | 11850 | // 3) Is our outer window background? If so, we're hidden. |
michael@0 | 11851 | // Otherwise, we're visible. |
michael@0 | 11852 | if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() || |
michael@0 | 11853 | mWindow->GetOuterWindow()->IsBackground()) { |
michael@0 | 11854 | return dom::VisibilityState::Hidden; |
michael@0 | 11855 | } |
michael@0 | 11856 | |
michael@0 | 11857 | return dom::VisibilityState::Visible; |
michael@0 | 11858 | } |
michael@0 | 11859 | |
michael@0 | 11860 | /* virtual */ void |
michael@0 | 11861 | nsDocument::PostVisibilityUpdateEvent() |
michael@0 | 11862 | { |
michael@0 | 11863 | nsCOMPtr<nsIRunnable> event = |
michael@0 | 11864 | NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState); |
michael@0 | 11865 | NS_DispatchToMainThread(event); |
michael@0 | 11866 | } |
michael@0 | 11867 | |
michael@0 | 11868 | NS_IMETHODIMP |
michael@0 | 11869 | nsDocument::GetMozHidden(bool* aHidden) |
michael@0 | 11870 | { |
michael@0 | 11871 | *aHidden = MozHidden(); |
michael@0 | 11872 | return NS_OK; |
michael@0 | 11873 | } |
michael@0 | 11874 | |
michael@0 | 11875 | NS_IMETHODIMP |
michael@0 | 11876 | nsDocument::GetHidden(bool* aHidden) |
michael@0 | 11877 | { |
michael@0 | 11878 | *aHidden = Hidden(); |
michael@0 | 11879 | return NS_OK; |
michael@0 | 11880 | } |
michael@0 | 11881 | |
michael@0 | 11882 | NS_IMETHODIMP |
michael@0 | 11883 | nsDocument::GetMozVisibilityState(nsAString& aState) |
michael@0 | 11884 | { |
michael@0 | 11885 | WarnOnceAbout(ePrefixedVisibilityAPI); |
michael@0 | 11886 | return GetVisibilityState(aState); |
michael@0 | 11887 | } |
michael@0 | 11888 | |
michael@0 | 11889 | NS_IMETHODIMP |
michael@0 | 11890 | nsDocument::GetVisibilityState(nsAString& aState) |
michael@0 | 11891 | { |
michael@0 | 11892 | const EnumEntry& entry = |
michael@0 | 11893 | VisibilityStateValues::strings[static_cast<int>(mVisibilityState)]; |
michael@0 | 11894 | aState.AssignASCII(entry.value, entry.length); |
michael@0 | 11895 | return NS_OK; |
michael@0 | 11896 | } |
michael@0 | 11897 | |
michael@0 | 11898 | /* virtual */ void |
michael@0 | 11899 | nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const |
michael@0 | 11900 | { |
michael@0 | 11901 | aWindowSizes->mDOMOtherSize += |
michael@0 | 11902 | nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 11903 | |
michael@0 | 11904 | if (mPresShell) { |
michael@0 | 11905 | mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf, |
michael@0 | 11906 | &aWindowSizes->mArenaStats, |
michael@0 | 11907 | &aWindowSizes->mLayoutPresShellSize, |
michael@0 | 11908 | &aWindowSizes->mLayoutStyleSetsSize, |
michael@0 | 11909 | &aWindowSizes->mLayoutTextRunsSize, |
michael@0 | 11910 | &aWindowSizes->mLayoutPresContextSize); |
michael@0 | 11911 | } |
michael@0 | 11912 | |
michael@0 | 11913 | aWindowSizes->mPropertyTablesSize += |
michael@0 | 11914 | mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 11915 | for (uint32_t i = 0, count = mExtraPropertyTables.Length(); |
michael@0 | 11916 | i < count; ++i) { |
michael@0 | 11917 | aWindowSizes->mPropertyTablesSize += |
michael@0 | 11918 | mExtraPropertyTables[i]->SizeOfExcludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 11919 | } |
michael@0 | 11920 | |
michael@0 | 11921 | if (EventListenerManager* elm = GetExistingListenerManager()) { |
michael@0 | 11922 | aWindowSizes->mDOMEventListenersCount += elm->ListenerCount(); |
michael@0 | 11923 | } |
michael@0 | 11924 | |
michael@0 | 11925 | // Measurement of the following members may be added later if DMD finds it |
michael@0 | 11926 | // is worthwhile: |
michael@0 | 11927 | // - many! |
michael@0 | 11928 | } |
michael@0 | 11929 | |
michael@0 | 11930 | void |
michael@0 | 11931 | nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const |
michael@0 | 11932 | { |
michael@0 | 11933 | aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this); |
michael@0 | 11934 | DocAddSizeOfExcludingThis(aWindowSizes); |
michael@0 | 11935 | } |
michael@0 | 11936 | |
michael@0 | 11937 | static size_t |
michael@0 | 11938 | SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet, |
michael@0 | 11939 | MallocSizeOf aMallocSizeOf, |
michael@0 | 11940 | void* aData) |
michael@0 | 11941 | { |
michael@0 | 11942 | return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 11943 | } |
michael@0 | 11944 | |
michael@0 | 11945 | size_t |
michael@0 | 11946 | nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
michael@0 | 11947 | { |
michael@0 | 11948 | // This SizeOfExcludingThis() overrides the one from nsINode. But |
michael@0 | 11949 | // nsDocuments can only appear at the top of the DOM tree, and we use the |
michael@0 | 11950 | // specialized DocAddSizeOfExcludingThis() in that case. So this should never |
michael@0 | 11951 | // be called. |
michael@0 | 11952 | MOZ_CRASH(); |
michael@0 | 11953 | } |
michael@0 | 11954 | |
michael@0 | 11955 | void |
michael@0 | 11956 | nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const |
michael@0 | 11957 | { |
michael@0 | 11958 | nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes); |
michael@0 | 11959 | |
michael@0 | 11960 | for (nsIContent* node = nsINode::GetFirstChild(); |
michael@0 | 11961 | node; |
michael@0 | 11962 | node = node->GetNextNode(this)) |
michael@0 | 11963 | { |
michael@0 | 11964 | size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 11965 | size_t* p; |
michael@0 | 11966 | |
michael@0 | 11967 | switch (node->NodeType()) { |
michael@0 | 11968 | case nsIDOMNode::ELEMENT_NODE: |
michael@0 | 11969 | p = &aWindowSizes->mDOMElementNodesSize; |
michael@0 | 11970 | break; |
michael@0 | 11971 | case nsIDOMNode::TEXT_NODE: |
michael@0 | 11972 | p = &aWindowSizes->mDOMTextNodesSize; |
michael@0 | 11973 | break; |
michael@0 | 11974 | case nsIDOMNode::CDATA_SECTION_NODE: |
michael@0 | 11975 | p = &aWindowSizes->mDOMCDATANodesSize; |
michael@0 | 11976 | break; |
michael@0 | 11977 | case nsIDOMNode::COMMENT_NODE: |
michael@0 | 11978 | p = &aWindowSizes->mDOMCommentNodesSize; |
michael@0 | 11979 | break; |
michael@0 | 11980 | default: |
michael@0 | 11981 | p = &aWindowSizes->mDOMOtherSize; |
michael@0 | 11982 | break; |
michael@0 | 11983 | } |
michael@0 | 11984 | |
michael@0 | 11985 | *p += nodeSize; |
michael@0 | 11986 | |
michael@0 | 11987 | if (EventListenerManager* elm = node->GetExistingListenerManager()) { |
michael@0 | 11988 | aWindowSizes->mDOMEventListenersCount += elm->ListenerCount(); |
michael@0 | 11989 | } |
michael@0 | 11990 | } |
michael@0 | 11991 | |
michael@0 | 11992 | aWindowSizes->mStyleSheetsSize += |
michael@0 | 11993 | mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
michael@0 | 11994 | aWindowSizes->mMallocSizeOf); |
michael@0 | 11995 | aWindowSizes->mStyleSheetsSize += |
michael@0 | 11996 | mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
michael@0 | 11997 | aWindowSizes->mMallocSizeOf); |
michael@0 | 11998 | aWindowSizes->mStyleSheetsSize += |
michael@0 | 11999 | mAdditionalSheets[eAgentSheet]. |
michael@0 | 12000 | SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
michael@0 | 12001 | aWindowSizes->mMallocSizeOf); |
michael@0 | 12002 | aWindowSizes->mStyleSheetsSize += |
michael@0 | 12003 | mAdditionalSheets[eUserSheet]. |
michael@0 | 12004 | SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
michael@0 | 12005 | aWindowSizes->mMallocSizeOf); |
michael@0 | 12006 | aWindowSizes->mStyleSheetsSize += |
michael@0 | 12007 | mAdditionalSheets[eAuthorSheet]. |
michael@0 | 12008 | SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, |
michael@0 | 12009 | aWindowSizes->mMallocSizeOf); |
michael@0 | 12010 | // Lumping in the loader with the style-sheets size is not ideal, |
michael@0 | 12011 | // but most of the things in there are in fact stylesheets, so it |
michael@0 | 12012 | // doesn't seem worthwhile to separate it out. |
michael@0 | 12013 | aWindowSizes->mStyleSheetsSize += |
michael@0 | 12014 | CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 12015 | |
michael@0 | 12016 | aWindowSizes->mDOMOtherSize += |
michael@0 | 12017 | mAttrStyleSheet ? |
michael@0 | 12018 | mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) : |
michael@0 | 12019 | 0; |
michael@0 | 12020 | |
michael@0 | 12021 | aWindowSizes->mDOMOtherSize += |
michael@0 | 12022 | mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf); |
michael@0 | 12023 | |
michael@0 | 12024 | aWindowSizes->mDOMOtherSize += |
michael@0 | 12025 | mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis, |
michael@0 | 12026 | aWindowSizes->mMallocSizeOf); |
michael@0 | 12027 | |
michael@0 | 12028 | // Measurement of the following members may be added later if DMD finds it |
michael@0 | 12029 | // is worthwhile: |
michael@0 | 12030 | // - many! |
michael@0 | 12031 | } |
michael@0 | 12032 | |
michael@0 | 12033 | NS_IMETHODIMP |
michael@0 | 12034 | nsDocument::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn) |
michael@0 | 12035 | { |
michael@0 | 12036 | return nsINode::QuerySelector(aSelector, aReturn); |
michael@0 | 12037 | } |
michael@0 | 12038 | |
michael@0 | 12039 | NS_IMETHODIMP |
michael@0 | 12040 | nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn) |
michael@0 | 12041 | { |
michael@0 | 12042 | return nsINode::QuerySelectorAll(aSelector, aReturn); |
michael@0 | 12043 | } |
michael@0 | 12044 | |
michael@0 | 12045 | already_AddRefed<nsIDocument> |
michael@0 | 12046 | nsIDocument::Constructor(const GlobalObject& aGlobal, |
michael@0 | 12047 | ErrorResult& rv) |
michael@0 | 12048 | { |
michael@0 | 12049 | nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); |
michael@0 | 12050 | if (!global) { |
michael@0 | 12051 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 12052 | return nullptr; |
michael@0 | 12053 | } |
michael@0 | 12054 | |
michael@0 | 12055 | nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports()); |
michael@0 | 12056 | if (!prin) { |
michael@0 | 12057 | rv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 12058 | return nullptr; |
michael@0 | 12059 | } |
michael@0 | 12060 | |
michael@0 | 12061 | nsCOMPtr<nsIURI> uri; |
michael@0 | 12062 | NS_NewURI(getter_AddRefs(uri), "about:blank"); |
michael@0 | 12063 | if (!uri) { |
michael@0 | 12064 | rv.Throw(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 12065 | return nullptr; |
michael@0 | 12066 | } |
michael@0 | 12067 | |
michael@0 | 12068 | nsCOMPtr<nsIDOMDocument> document; |
michael@0 | 12069 | nsresult res = |
michael@0 | 12070 | NS_NewDOMDocument(getter_AddRefs(document), |
michael@0 | 12071 | NullString(), |
michael@0 | 12072 | EmptyString(), |
michael@0 | 12073 | nullptr, |
michael@0 | 12074 | uri, |
michael@0 | 12075 | uri, |
michael@0 | 12076 | prin->GetPrincipal(), |
michael@0 | 12077 | true, |
michael@0 | 12078 | global, |
michael@0 | 12079 | DocumentFlavorLegacyGuess); |
michael@0 | 12080 | if (NS_FAILED(res)) { |
michael@0 | 12081 | rv.Throw(res); |
michael@0 | 12082 | return nullptr; |
michael@0 | 12083 | } |
michael@0 | 12084 | |
michael@0 | 12085 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(document); |
michael@0 | 12086 | doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE); |
michael@0 | 12087 | |
michael@0 | 12088 | return doc.forget(); |
michael@0 | 12089 | } |
michael@0 | 12090 | |
michael@0 | 12091 | already_AddRefed<nsIDOMXPathExpression> |
michael@0 | 12092 | nsIDocument::CreateExpression(const nsAString& aExpression, |
michael@0 | 12093 | nsIDOMXPathNSResolver* aResolver, |
michael@0 | 12094 | ErrorResult& rv) |
michael@0 | 12095 | { |
michael@0 | 12096 | return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv); |
michael@0 | 12097 | } |
michael@0 | 12098 | |
michael@0 | 12099 | already_AddRefed<nsIDOMXPathNSResolver> |
michael@0 | 12100 | nsIDocument::CreateNSResolver(nsINode* aNodeResolver, |
michael@0 | 12101 | ErrorResult& rv) |
michael@0 | 12102 | { |
michael@0 | 12103 | return XPathEvaluator()->CreateNSResolver(aNodeResolver, rv); |
michael@0 | 12104 | } |
michael@0 | 12105 | |
michael@0 | 12106 | already_AddRefed<nsISupports> |
michael@0 | 12107 | nsIDocument::Evaluate(const nsAString& aExpression, nsINode* aContextNode, |
michael@0 | 12108 | nsIDOMXPathNSResolver* aResolver, uint16_t aType, |
michael@0 | 12109 | nsISupports* aResult, ErrorResult& rv) |
michael@0 | 12110 | { |
michael@0 | 12111 | return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType, |
michael@0 | 12112 | aResult, rv); |
michael@0 | 12113 | } |
michael@0 | 12114 | |
michael@0 | 12115 | NS_IMETHODIMP |
michael@0 | 12116 | nsDocument::CreateExpression(const nsAString& aExpression, |
michael@0 | 12117 | nsIDOMXPathNSResolver* aResolver, |
michael@0 | 12118 | nsIDOMXPathExpression** aResult) |
michael@0 | 12119 | { |
michael@0 | 12120 | return XPathEvaluator()->CreateExpression(aExpression, aResolver, aResult); |
michael@0 | 12121 | } |
michael@0 | 12122 | |
michael@0 | 12123 | NS_IMETHODIMP |
michael@0 | 12124 | nsDocument::CreateNSResolver(nsIDOMNode* aNodeResolver, |
michael@0 | 12125 | nsIDOMXPathNSResolver** aResult) |
michael@0 | 12126 | { |
michael@0 | 12127 | return XPathEvaluator()->CreateNSResolver(aNodeResolver, aResult); |
michael@0 | 12128 | } |
michael@0 | 12129 | |
michael@0 | 12130 | NS_IMETHODIMP |
michael@0 | 12131 | nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode, |
michael@0 | 12132 | nsIDOMXPathNSResolver* aResolver, uint16_t aType, |
michael@0 | 12133 | nsISupports* aInResult, nsISupports** aResult) |
michael@0 | 12134 | { |
michael@0 | 12135 | return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType, |
michael@0 | 12136 | aInResult, aResult); |
michael@0 | 12137 | } |
michael@0 | 12138 | |
michael@0 | 12139 | // This is just a hack around the fact that window.document is not |
michael@0 | 12140 | // [Unforgeable] yet. |
michael@0 | 12141 | JSObject* |
michael@0 | 12142 | nsIDocument::WrapObject(JSContext *aCx) |
michael@0 | 12143 | { |
michael@0 | 12144 | MOZ_ASSERT(IsDOMBinding()); |
michael@0 | 12145 | |
michael@0 | 12146 | JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx)); |
michael@0 | 12147 | if (!obj) { |
michael@0 | 12148 | return nullptr; |
michael@0 | 12149 | } |
michael@0 | 12150 | |
michael@0 | 12151 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(GetInnerWindow()); |
michael@0 | 12152 | if (!win) { |
michael@0 | 12153 | // No window, nothing else to do here |
michael@0 | 12154 | return obj; |
michael@0 | 12155 | } |
michael@0 | 12156 | |
michael@0 | 12157 | if (this != win->GetExtantDoc()) { |
michael@0 | 12158 | // We're not the current document; we're also done here |
michael@0 | 12159 | return obj; |
michael@0 | 12160 | } |
michael@0 | 12161 | |
michael@0 | 12162 | JSAutoCompartment ac(aCx, obj); |
michael@0 | 12163 | |
michael@0 | 12164 | JS::Rooted<JS::Value> winVal(aCx); |
michael@0 | 12165 | nsresult rv = nsContentUtils::WrapNative(aCx, win, &NS_GET_IID(nsIDOMWindow), |
michael@0 | 12166 | &winVal, |
michael@0 | 12167 | false); |
michael@0 | 12168 | if (NS_FAILED(rv)) { |
michael@0 | 12169 | Throw(aCx, rv); |
michael@0 | 12170 | return nullptr; |
michael@0 | 12171 | } |
michael@0 | 12172 | |
michael@0 | 12173 | NS_NAMED_LITERAL_STRING(doc_str, "document"); |
michael@0 | 12174 | |
michael@0 | 12175 | if (!JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(winVal), doc_str.get(), |
michael@0 | 12176 | doc_str.Length(), JS::ObjectValue(*obj), |
michael@0 | 12177 | JS_PropertyStub, JS_StrictPropertyStub, |
michael@0 | 12178 | JSPROP_READONLY | JSPROP_ENUMERATE)) { |
michael@0 | 12179 | return nullptr; |
michael@0 | 12180 | } |
michael@0 | 12181 | |
michael@0 | 12182 | return obj; |
michael@0 | 12183 | } |
michael@0 | 12184 | |
michael@0 | 12185 | XPathEvaluator* |
michael@0 | 12186 | nsIDocument::XPathEvaluator() |
michael@0 | 12187 | { |
michael@0 | 12188 | if (!mXPathEvaluator) { |
michael@0 | 12189 | mXPathEvaluator = new dom::XPathEvaluator(this); |
michael@0 | 12190 | } |
michael@0 | 12191 | return mXPathEvaluator; |
michael@0 | 12192 | } |
michael@0 | 12193 | |
michael@0 | 12194 | already_AddRefed<nsIDocumentEncoder> |
michael@0 | 12195 | nsIDocument::GetCachedEncoder() |
michael@0 | 12196 | { |
michael@0 | 12197 | return mCachedEncoder.forget(); |
michael@0 | 12198 | } |
michael@0 | 12199 | |
michael@0 | 12200 | void |
michael@0 | 12201 | nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder) |
michael@0 | 12202 | { |
michael@0 | 12203 | mCachedEncoder = aEncoder; |
michael@0 | 12204 | } |
michael@0 | 12205 | |
michael@0 | 12206 | void |
michael@0 | 12207 | nsIDocument::SetContentTypeInternal(const nsACString& aType) |
michael@0 | 12208 | { |
michael@0 | 12209 | mCachedEncoder = nullptr; |
michael@0 | 12210 | mContentType = aType; |
michael@0 | 12211 | } |
michael@0 | 12212 | |
michael@0 | 12213 | nsILoadContext* |
michael@0 | 12214 | nsIDocument::GetLoadContext() const |
michael@0 | 12215 | { |
michael@0 | 12216 | return mDocumentContainer; |
michael@0 | 12217 | } |
michael@0 | 12218 | |
michael@0 | 12219 | nsIDocShell* |
michael@0 | 12220 | nsIDocument::GetDocShell() const |
michael@0 | 12221 | { |
michael@0 | 12222 | return mDocumentContainer; |
michael@0 | 12223 | } |
michael@0 | 12224 | |
michael@0 | 12225 | void |
michael@0 | 12226 | nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer) |
michael@0 | 12227 | { |
michael@0 | 12228 | mStateObjectContainer = scContainer; |
michael@0 | 12229 | mStateObjectCached = nullptr; |
michael@0 | 12230 | } |
michael@0 | 12231 | |
michael@0 | 12232 | already_AddRefed<Element> |
michael@0 | 12233 | nsIDocument::CreateHTMLElement(nsIAtom* aTag) |
michael@0 | 12234 | { |
michael@0 | 12235 | nsCOMPtr<nsINodeInfo> nodeInfo; |
michael@0 | 12236 | nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML, |
michael@0 | 12237 | nsIDOMNode::ELEMENT_NODE); |
michael@0 | 12238 | MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail"); |
michael@0 | 12239 | |
michael@0 | 12240 | nsCOMPtr<Element> element; |
michael@0 | 12241 | DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element), |
michael@0 | 12242 | nodeInfo.forget(), |
michael@0 | 12243 | mozilla::dom::NOT_FROM_PARSER); |
michael@0 | 12244 | |
michael@0 | 12245 | MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail"); |
michael@0 | 12246 | return element.forget(); |
michael@0 | 12247 | } |
michael@0 | 12248 | |
michael@0 | 12249 | bool |
michael@0 | 12250 | MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData) |
michael@0 | 12251 | { |
michael@0 | 12252 | nsCOMArray<nsIDocument>* documents = |
michael@0 | 12253 | static_cast<nsCOMArray<nsIDocument>*>(aData); |
michael@0 | 12254 | if (aDoc) { |
michael@0 | 12255 | aDoc->SetIsInSyncOperation(true); |
michael@0 | 12256 | documents->AppendObject(aDoc); |
michael@0 | 12257 | aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData); |
michael@0 | 12258 | } |
michael@0 | 12259 | return true; |
michael@0 | 12260 | } |
michael@0 | 12261 | |
michael@0 | 12262 | nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc) |
michael@0 | 12263 | { |
michael@0 | 12264 | mMicroTaskLevel = nsContentUtils::MicroTaskLevel(); |
michael@0 | 12265 | nsContentUtils::SetMicroTaskLevel(0); |
michael@0 | 12266 | if (aDoc) { |
michael@0 | 12267 | nsPIDOMWindow* win = aDoc->GetWindow(); |
michael@0 | 12268 | if (win) { |
michael@0 | 12269 | nsCOMPtr<nsIDOMWindow> topWindow; |
michael@0 | 12270 | win->GetTop(getter_AddRefs(topWindow)); |
michael@0 | 12271 | nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow); |
michael@0 | 12272 | if (top) { |
michael@0 | 12273 | nsCOMPtr<nsIDocument> doc = top->GetExtantDoc(); |
michael@0 | 12274 | MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments); |
michael@0 | 12275 | } |
michael@0 | 12276 | } |
michael@0 | 12277 | } |
michael@0 | 12278 | } |
michael@0 | 12279 | |
michael@0 | 12280 | nsAutoSyncOperation::~nsAutoSyncOperation() |
michael@0 | 12281 | { |
michael@0 | 12282 | for (int32_t i = 0; i < mDocuments.Count(); ++i) { |
michael@0 | 12283 | mDocuments[i]->SetIsInSyncOperation(false); |
michael@0 | 12284 | } |
michael@0 | 12285 | nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel); |
michael@0 | 12286 | } |
michael@0 | 12287 |