content/base/src/nsDocument.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 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

mercurial