nsXULElement.h (17719B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 8 The base XUL element class and associates. 9 10 */ 11 12 #ifndef nsXULElement_h__ 13 #define nsXULElement_h__ 14 15 #include <stdint.h> 16 #include <stdio.h> 17 18 #include "ErrorList.h" 19 #include "js/RootingAPI.h" 20 #include "js/SourceText.h" 21 #include "js/TracingAPI.h" 22 #include "js/TypeDecls.h" 23 #include "js/Utility.h" // JS::FreePolicy 24 #include "js/experimental/JSStencil.h" 25 #include "mozilla/AlreadyAddRefed.h" 26 #include "mozilla/Attributes.h" 27 #include "mozilla/BasicEvents.h" 28 #include "mozilla/RefPtr.h" 29 #include "mozilla/UniquePtr.h" 30 #include "mozilla/dom/DOMString.h" 31 #include "mozilla/dom/Element.h" 32 #include "mozilla/dom/FragmentOrElement.h" 33 #include "mozilla/dom/FromParser.h" 34 #include "mozilla/dom/NameSpaceConstants.h" 35 #include "mozilla/dom/NodeInfo.h" 36 #include "nsAtom.h" 37 #include "nsAttrName.h" 38 #include "nsAttrValue.h" 39 #include "nsCOMPtr.h" 40 #include "nsCaseTreatment.h" 41 #include "nsChangeHint.h" 42 #include "nsCycleCollectionParticipant.h" 43 #include "nsGkAtoms.h" 44 #include "nsIContent.h" 45 #include "nsINode.h" 46 #include "nsISupports.h" 47 #include "nsLiteralString.h" 48 #include "nsString.h" 49 #include "nsStyledElement.h" 50 #include "nsTArray.h" 51 #include "nsTLiteralString.h" 52 #include "nsWindowSizes.h" 53 #include "nscore.h" 54 55 class JSObject; 56 class nsIControllers; 57 class nsIObjectInputStream; 58 class nsIObjectOutputStream; 59 class nsIOffThreadScriptReceiver; 60 class nsIPrincipal; 61 class nsIURI; 62 class nsXULPrototypeDocument; 63 class nsXULPrototypeNode; 64 struct JSContext; 65 66 using nsPrototypeArray = nsTArray<RefPtr<nsXULPrototypeNode>>; 67 68 namespace JS { 69 class CompileOptions; 70 } 71 72 namespace mozilla { 73 class ErrorResult; 74 class EventChainPreVisitor; 75 class EventListenerManager; 76 namespace css { 77 class StyleRule; 78 } // namespace css 79 namespace dom { 80 class Document; 81 class HTMLIFrameElement; 82 class PrototypeDocumentContentSink; 83 enum class CallerType : uint32_t; 84 } // namespace dom 85 } // namespace mozilla 86 87 //////////////////////////////////////////////////////////////////////// 88 89 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING 90 # define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) \ 91 (nsXULPrototypeAttribute::counter++) 92 #else 93 # define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) ((void)0) 94 #endif 95 96 /** 97 98 A prototype attribute for an nsXULPrototypeElement. 99 100 */ 101 102 class nsXULPrototypeAttribute { 103 public: 104 nsXULPrototypeAttribute() 105 : mName(nsGkAtoms::id) // XXX this is a hack, but names have to have a 106 // value 107 { 108 XUL_PROTOTYPE_ATTRIBUTE_METER(gNumAttributes); 109 MOZ_COUNT_CTOR(nsXULPrototypeAttribute); 110 } 111 112 ~nsXULPrototypeAttribute(); 113 114 nsAttrName mName; 115 nsAttrValue mValue; 116 117 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING 118 static uint32_t gNumElements; 119 static uint32_t gNumAttributes; 120 static uint32_t gNumCacheTests; 121 static uint32_t gNumCacheHits; 122 static uint32_t gNumCacheSets; 123 static uint32_t gNumCacheFills; 124 #endif /* !XUL_PROTOTYPE_ATTRIBUTE_METERING */ 125 }; 126 127 /** 128 129 A prototype content model element that holds the "primordial" values 130 that have been parsed from the original XUL document. 131 132 */ 133 134 class nsXULPrototypeNode { 135 public: 136 enum Type { eType_Element, eType_Script, eType_Text, eType_PI }; 137 138 Type mType; 139 140 virtual nsresult Serialize( 141 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 142 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) = 0; 143 virtual nsresult Deserialize( 144 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 145 nsIURI* aDocumentURI, 146 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) = 0; 147 148 /** 149 * The prototype document must call ReleaseSubtree when it is going 150 * away. This makes the parents through the tree stop owning their 151 * children, whether or not the parent's reference count is zero. 152 * Individual elements may still own individual prototypes, but 153 * those prototypes no longer remember their children to allow them 154 * to be constructed. 155 */ 156 virtual void ReleaseSubtree() {} 157 158 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsXULPrototypeNode) 159 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsXULPrototypeNode) 160 161 protected: 162 explicit nsXULPrototypeNode(Type aType) : mType(aType) {} 163 virtual ~nsXULPrototypeNode() = default; 164 }; 165 166 class nsXULPrototypeElement : public nsXULPrototypeNode { 167 public: 168 explicit nsXULPrototypeElement(mozilla::dom::NodeInfo* aNodeInfo = nullptr) 169 : nsXULPrototypeNode(eType_Element), 170 mNodeInfo(aNodeInfo), 171 mIsAtom(nullptr) {} 172 173 private: 174 virtual ~nsXULPrototypeElement() { Unlink(); } 175 176 public: 177 void ReleaseSubtree() override { 178 for (int32_t i = mChildren.Length() - 1; i >= 0; i--) { 179 if (mChildren[i].get()) mChildren[i]->ReleaseSubtree(); 180 } 181 mChildren.Clear(); 182 nsXULPrototypeNode::ReleaseSubtree(); 183 } 184 185 nsresult Serialize( 186 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 187 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 188 nsresult Deserialize( 189 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 190 nsIURI* aDocumentURI, 191 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 192 193 nsresult SetAttrAt(uint32_t aPos, const nsAString& aValue, 194 nsIURI* aDocumentURI); 195 196 void Unlink(); 197 198 nsPrototypeArray mChildren; 199 200 RefPtr<mozilla::dom::NodeInfo> mNodeInfo; 201 202 nsTArray<nsXULPrototypeAttribute> mAttributes; // [OWNER] 203 RefPtr<nsAtom> mIsAtom; 204 }; 205 206 class nsXULPrototypeScript : public nsXULPrototypeNode { 207 public: 208 explicit nsXULPrototypeScript(uint32_t aLineNo); 209 210 private: 211 virtual ~nsXULPrototypeScript() = default; 212 213 void FillCompileOptions(JS::CompileOptions& aOptions, const char* aFilename, 214 uint32_t aLineNo); 215 216 public: 217 nsresult Serialize( 218 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 219 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 220 nsresult SerializeOutOfLine(nsIObjectOutputStream* aStream, 221 nsXULPrototypeDocument* aProtoDoc); 222 nsresult Deserialize( 223 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 224 nsIURI* aDocumentURI, 225 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 226 nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput, 227 nsXULPrototypeDocument* aProtoDoc); 228 229 // Compile given JS source text synchronously. 230 // 231 // This method doesn't take the ownership of aText, but borrows during the 232 // compilation. 233 // 234 // If successfully compiled, `HasStencil()` returns true. 235 nsresult Compile(const char16_t* aText, size_t aTextLength, nsIURI* aURI, 236 uint32_t aLineNo, mozilla::dom::Document* aDocument); 237 238 // Compile given JS source text possibly in off-thread. 239 // 240 // This method takes the ownership of aText. 241 // 242 // If this doesn't use off-thread compilation and successfully compiled, 243 // `HasStencil()` returns true. 244 // 245 // If this uses off-thread compilation, `HasStencil()` returns false, and 246 // once the compilation finishes, aOffThreadReceiver gets notified with the 247 // compiled stencil. The callback is responsible for calling `Set()` with 248 // the stencil. 249 nsresult CompileMaybeOffThread( 250 mozilla::UniquePtr<mozilla::Utf8Unit[], JS::FreePolicy>&& aText, 251 size_t aTextLength, nsIURI* aURI, uint32_t aLineNo, 252 mozilla::dom::Document* aDocument, 253 nsIOffThreadScriptReceiver* aOffThreadReceiver); 254 255 void Set(JS::Stencil* aStencil); 256 257 bool HasStencil() { return mStencil; } 258 259 JS::Stencil* GetStencil() { return mStencil.get(); } 260 261 nsresult InstantiateScript(JSContext* aCx, 262 JS::MutableHandle<JSScript*> aScript); 263 264 void AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const; 265 266 nsCOMPtr<nsIURI> mSrcURI; 267 uint32_t mLineNo; 268 bool mSrcLoading; 269 bool mOutOfLine; 270 mozilla::dom::PrototypeDocumentContentSink* 271 mSrcLoadWaiters; // [OWNER] but not COMPtr 272 private: 273 RefPtr<JS::Stencil> mStencil; 274 }; 275 276 class nsXULPrototypeText : public nsXULPrototypeNode { 277 public: 278 nsXULPrototypeText() : nsXULPrototypeNode(eType_Text) {} 279 280 private: 281 virtual ~nsXULPrototypeText() = default; 282 283 public: 284 nsresult Serialize( 285 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 286 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 287 nsresult Deserialize( 288 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 289 nsIURI* aDocumentURI, 290 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 291 292 nsString mValue; 293 }; 294 295 class nsXULPrototypePI : public nsXULPrototypeNode { 296 public: 297 nsXULPrototypePI() : nsXULPrototypeNode(eType_PI) {} 298 299 private: 300 virtual ~nsXULPrototypePI() = default; 301 302 public: 303 nsresult Serialize( 304 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 305 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 306 nsresult Deserialize( 307 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc, 308 nsIURI* aDocumentURI, 309 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) override; 310 311 nsString mTarget; 312 nsString mData; 313 }; 314 315 //////////////////////////////////////////////////////////////////////// 316 317 /** 318 319 The XUL element. 320 321 */ 322 323 #define XUL_ELEMENT_FLAG_BIT(n_) \ 324 NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_)) 325 326 // XUL element specific bits 327 enum { 328 XUL_ELEMENT_HAS_CONTENTMENU_LISTENER = XUL_ELEMENT_FLAG_BIT(0), 329 XUL_ELEMENT_HAS_POPUP_LISTENER = XUL_ELEMENT_FLAG_BIT(1) 330 }; 331 332 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2); 333 334 #undef XUL_ELEMENT_FLAG_BIT 335 336 class nsXULElement : public nsStyledElement { 337 protected: 338 using Document = mozilla::dom::Document; 339 340 // Use Construct to construct elements instead of this constructor. 341 explicit nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); 342 343 public: 344 using Element::Blur; 345 using Element::Focus; 346 347 static already_AddRefed<mozilla::dom::Element> CreateFromPrototype( 348 nsXULPrototypeElement* aPrototype, Document* aDocument, bool aIsRoot); 349 350 // This is the constructor for nsXULElements. 351 static nsXULElement* Construct( 352 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); 353 354 NS_IMPL_FROMNODE(nsXULElement, kNameSpaceID_XUL) 355 356 // nsISupports 357 NS_DECL_ISUPPORTS_INHERITED 358 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULElement, nsStyledElement) 359 360 // nsINode 361 void GetEventTargetParent(mozilla::EventChainPreVisitor& aVisitor) override; 362 MOZ_CAN_RUN_SCRIPT_BOUNDARY 363 nsresult PreHandleEvent(mozilla::EventChainVisitor& aVisitor) override; 364 // nsIContent 365 void DestroyContent() override; 366 nsresult BindToTree(BindContext&, nsINode& aParent) override; 367 void UnbindFromTree(UnbindContext&) override; 368 void DoneAddingChildren(bool aHaveNotified) override; 369 370 #ifdef MOZ_DOM_LIST 371 void List(FILE* out, int32_t aIndent) const override; 372 void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override {} 373 #endif 374 375 MOZ_CAN_RUN_SCRIPT bool HasMenu(); 376 MOZ_CAN_RUN_SCRIPT void OpenMenu(bool aOpenFlag); 377 378 MOZ_CAN_RUN_SCRIPT 379 mozilla::Result<bool, nsresult> PerformAccesskey( 380 bool aKeyCausesActivation, bool aIsTrustedEvent) override; 381 MOZ_CAN_RUN_SCRIPT void ClickWithInputSource(uint16_t aInputSource, 382 bool aIsTrustedEvent); 383 struct XULFocusability { 384 bool mDefaultFocusable = false; 385 mozilla::Maybe<bool> mForcedFocusable; 386 mozilla::Maybe<int32_t> mForcedTabIndexIfFocusable; 387 388 static XULFocusability NeverFocusable() { 389 return {false, mozilla::Some(false), mozilla::Some(-1)}; 390 } 391 }; 392 XULFocusability GetXULFocusability(mozilla::IsFocusableFlags); 393 Focusable IsFocusableWithoutStyle(mozilla::IsFocusableFlags) override; 394 395 NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override; 396 397 nsresult Clone(mozilla::dom::NodeInfo*, nsINode** aResult) const override; 398 399 bool IsEventAttributeNameInternal(nsAtom* aName) override; 400 401 using DOMString = mozilla::dom::DOMString; 402 void GetXULAttr(nsAtom* aName, DOMString& aResult) const { 403 GetAttr(aName, aResult); 404 } 405 void SetXULAttr(nsAtom* aName, const nsAString& aValue, 406 mozilla::ErrorResult& aError) { 407 SetAttr(aName, aValue, aError); 408 } 409 bool GetXULBoolAttr(nsAtom* aName) const { 410 return AttrValueIs(kNameSpaceID_None, aName, u"true"_ns, eCaseMatters); 411 } 412 void SetXULBoolAttr(nsAtom* aName, bool aValue, 413 mozilla::ErrorResult& aError) { 414 if (aValue) { 415 SetAttr(aName, u"true"_ns, aError); 416 } else { 417 UnsetAttr(aName, aError); 418 } 419 } 420 421 // WebIDL API 422 bool Autofocus() const { return GetBoolAttr(nsGkAtoms::autofocus); } 423 void SetAutofocus(bool aAutofocus, mozilla::ErrorResult&) { 424 SetBoolAttr(nsGkAtoms::autofocus, aAutofocus); 425 } 426 bool Hidden() const { return GetBoolAttr(nsGkAtoms::hidden); } 427 void SetHidden(bool aHidden) { SetBoolAttr(nsGkAtoms::hidden, aHidden); } 428 bool Collapsed() const { return GetBoolAttr(nsGkAtoms::collapsed); } 429 void SetCollapsed(bool aCollapsed) { 430 SetBoolAttr(nsGkAtoms::collapsed, aCollapsed); 431 } 432 void GetObserves(DOMString& aValue) const { 433 GetXULAttr(nsGkAtoms::observes, aValue); 434 } 435 void SetObserves(const nsAString& aValue, mozilla::ErrorResult& rv) { 436 SetXULAttr(nsGkAtoms::observes, aValue, rv); 437 } 438 void GetMenu(DOMString& aValue) const { GetXULAttr(nsGkAtoms::menu, aValue); } 439 void SetMenu(const nsAString& aValue, mozilla::ErrorResult& rv) { 440 SetXULAttr(nsGkAtoms::menu, aValue, rv); 441 } 442 void GetContextMenu(DOMString& aValue) { 443 GetXULAttr(nsGkAtoms::contextmenu, aValue); 444 } 445 void SetContextMenu(const nsAString& aValue, mozilla::ErrorResult& rv) { 446 SetXULAttr(nsGkAtoms::contextmenu, aValue, rv); 447 } 448 void GetTooltip(DOMString& aValue) const { 449 GetXULAttr(nsGkAtoms::tooltip, aValue); 450 } 451 void SetTooltip(const nsAString& aValue, mozilla::ErrorResult& rv) { 452 SetXULAttr(nsGkAtoms::tooltip, aValue, rv); 453 } 454 void GetTooltipText(DOMString& aValue) const { 455 GetXULAttr(nsGkAtoms::tooltiptext, aValue); 456 } 457 void SetTooltipText(const nsAString& aValue, mozilla::ErrorResult& rv) { 458 SetXULAttr(nsGkAtoms::tooltiptext, aValue, rv); 459 } 460 void GetSrc(DOMString& aValue) const { GetXULAttr(nsGkAtoms::src, aValue); } 461 void SetSrc(const nsAString& aValue, mozilla::ErrorResult& rv) { 462 SetXULAttr(nsGkAtoms::src, aValue, rv); 463 } 464 nsIControllers* GetExtantControllers() const { 465 const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); 466 return slots ? slots->mControllers.get() : nullptr; 467 } 468 nsIControllers* EnsureControllers(); 469 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 470 MOZ_CAN_RUN_SCRIPT_BOUNDARY void Click(mozilla::dom::CallerType aCallerType); 471 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 472 MOZ_CAN_RUN_SCRIPT_BOUNDARY void DoCommand(); 473 // Style() inherited from nsStyledElement 474 475 nsINode* GetScopeChainParent() const override { 476 // For XUL, the parent is the parent element, if any 477 Element* parent = GetParentElement(); 478 return parent ? parent : nsStyledElement::GetScopeChainParent(); 479 } 480 481 bool IsInteractiveHTMLContent() const override; 482 483 protected: 484 ~nsXULElement(); 485 486 // This can be removed if EnsureContentsGenerated dies. 487 friend class nsNSElementTearoff; 488 489 // Implementation methods 490 nsresult EnsureContentsGenerated(void) const; 491 492 nsresult AddPopupListener(nsAtom* aName); 493 494 /** 495 * Abandon our prototype linkage, and copy all attributes locally 496 */ 497 nsresult MakeHeavyweight(nsXULPrototypeElement* aPrototype); 498 499 void BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, 500 const nsAttrValue* aValue, bool aNotify) override; 501 void AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 502 const nsAttrValue* aValue, const nsAttrValue* aOldValue, 503 nsIPrincipal* aSubjectPrincipal, bool aNotify) override; 504 505 bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 506 const nsAString& aValue, 507 nsIPrincipal* aMaybeScriptedPrincipal, 508 nsAttrValue& aResult) override; 509 510 mozilla::EventListenerManager* GetEventListenerManagerForAttr( 511 nsAtom* aAttrName, bool* aDefer) override; 512 513 /** 514 * Add a listener for the specified attribute, if appropriate. 515 */ 516 void AddListenerForAttributeIfNeeded(nsAtom* aLocalName); 517 518 protected: 519 void AddTooltipSupport(); 520 void RemoveTooltipSupport(); 521 522 bool SupportsAccessKey() const; 523 void RegUnRegAccessKey(bool aDoReg) override; 524 525 friend nsXULElement* NS_NewBasicXULElement( 526 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); 527 528 friend nsresult NS_NewXULElement(mozilla::dom::Element** aResult, 529 mozilla::dom::NodeInfo* aNodeInfo, 530 mozilla::dom::FromParser aFromParser, 531 const nsAString* aIs); 532 friend void NS_TrustedNewXULElement(mozilla::dom::Element** aResult, 533 mozilla::dom::NodeInfo* aNodeInfo); 534 535 JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) override; 536 537 bool IsEventStoppedFromAnonymousScrollbar(mozilla::EventMessage aMessage); 538 539 MOZ_CAN_RUN_SCRIPT 540 nsresult DispatchXULCommand(const mozilla::EventChainVisitor& aVisitor, 541 nsAutoString& aCommand); 542 }; 543 544 #endif // nsXULElement_h__