Bug 1878930 - s/RawBuffer/Span/: UniformData. r=gfx-reviewers,lsalzman
[gecko.git] / dom / xul / nsXULElement.cpp
blob42ddf38b6323539babcea37acd503ecbd61323b0
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/. */
6 #include "nsXULElement.h"
8 #include <new>
9 #include <utility>
10 #include "AttrArray.h"
11 #include "MainThreadUtils.h"
12 #include "ReferrerInfo.h"
13 #include "Units.h"
14 #include "XULButtonElement.h"
15 #include "XULFrameElement.h"
16 #include "XULMenuElement.h"
17 #include "XULMenuBarElement.h"
18 #include "XULPopupElement.h"
19 #include "XULResizerElement.h"
20 #include "XULTextElement.h"
21 #include "XULTooltipElement.h"
22 #include "XULTreeElement.h"
23 #include "js/CompilationAndEvaluation.h"
24 #include "js/CompileOptions.h" // JS::CompileOptions, JS::OwningCompileOptions, , JS::ReadOnlyCompileOptions, JS::ReadOnlyDecodeOptions, JS::DecodeOptions
25 #include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::SetNativeStackQuota, JS::ThreadStackQuotaForSize, JS::CompileGlobalScriptToStencil, JS::CompilationStorage
26 #include "js/experimental/JSStencil.h" // JS::Stencil, JS::FrontendContext
27 #include "js/SourceText.h"
28 #include "js/Transcoding.h"
29 #include "js/Utility.h"
30 #include "jsapi.h"
31 #include "mozilla/Assertions.h"
32 #include "mozilla/ArrayIterator.h"
33 #include "mozilla/ClearOnShutdown.h"
34 #include "mozilla/DeclarationBlock.h"
35 #include "mozilla/EventDispatcher.h"
36 #include "mozilla/EventListenerManager.h"
37 #include "mozilla/EventQueue.h"
38 #include "mozilla/EventStateManager.h"
39 #include "mozilla/FlushType.h"
40 #include "mozilla/GlobalKeyListener.h"
41 #include "mozilla/HoldDropJSObjects.h"
42 #include "mozilla/MacroForEach.h"
43 #include "mozilla/Maybe.h"
44 #include "mozilla/MouseEvents.h"
45 #include "mozilla/OwningNonNull.h"
46 #include "mozilla/PresShell.h"
47 #include "mozilla/RefPtr.h"
48 #include "mozilla/ScopeExit.h"
49 #include "mozilla/ShutdownPhase.h"
50 #include "mozilla/StaticAnalysisFunctions.h"
51 #include "mozilla/StaticPrefs_javascript.h"
52 #include "mozilla/StaticPtr.h"
53 #include "mozilla/TaskController.h"
54 #include "mozilla/UniquePtr.h"
55 #include "mozilla/URLExtraData.h"
56 #include "mozilla/dom/BindContext.h"
57 #include "mozilla/dom/BorrowedAttrInfo.h"
58 #include "mozilla/dom/CSSRuleBinding.h"
59 #include "mozilla/dom/Document.h"
60 #include "mozilla/dom/DocumentInlines.h"
61 #include "mozilla/dom/Element.h"
62 #include "mozilla/dom/Event.h"
63 #include "mozilla/dom/EventTarget.h"
64 #include "mozilla/dom/FragmentOrElement.h"
65 #include "mozilla/dom/FromParser.h"
66 #include "mozilla/dom/MouseEventBinding.h"
67 #include "mozilla/dom/MutationEventBinding.h"
68 #include "mozilla/dom/NodeInfo.h"
69 #include "mozilla/dom/ReferrerPolicyBinding.h"
70 #include "mozilla/dom/ScriptSettings.h"
71 #include "mozilla/dom/XULBroadcastManager.h"
72 #include "mozilla/dom/XULCommandEvent.h"
73 #include "mozilla/dom/XULElementBinding.h"
74 #include "mozilla/dom/nsCSPUtils.h"
75 #include "mozilla/fallible.h"
76 #include "nsAtom.h"
77 #include "nsAttrValueInlines.h"
78 #include "nsCaseTreatment.h"
79 #include "nsChangeHint.h"
80 #include "nsCOMPtr.h"
81 #include "nsCompatibility.h"
82 #include "nsContentCreatorFunctions.h"
83 #include "nsContentUtils.h"
84 #include "nsCycleCollectionNoteChild.h"
85 #include "nsCycleCollectionTraversalCallback.h"
86 #include "nsDebug.h"
87 #include "nsError.h"
88 #include "nsFocusManager.h"
89 #include "nsGkAtoms.h"
90 #include "nsIContent.h"
91 #include "nsIContentSecurityPolicy.h"
92 #include "nsIControllers.h"
93 #include "nsID.h"
94 #include "nsIDOMEventListener.h"
95 #include "nsIDOMXULControlElement.h"
96 #include "nsIDOMXULSelectCntrlItemEl.h"
97 #include "nsIDocShell.h"
98 #include "nsIFocusManager.h"
99 #include "nsIFrame.h"
100 #include "nsIObjectInputStream.h"
101 #include "nsIObjectOutputStream.h"
102 #include "nsIRunnable.h"
103 #include "nsIScriptContext.h"
104 #include "nsISupportsUtils.h"
105 #include "nsIURI.h"
106 #include "nsIXPConnect.h"
107 #include "nsMenuPopupFrame.h"
108 #include "nsNodeInfoManager.h"
109 #include "nsPIDOMWindow.h"
110 #include "nsPIDOMWindowInlines.h"
111 #include "nsPresContext.h"
112 #include "nsQueryFrame.h"
113 #include "nsString.h"
114 #include "nsStyledElement.h"
115 #include "nsThreadUtils.h"
116 #include "nsXULControllers.h"
117 #include "nsXULPopupListener.h"
118 #include "nsXULPopupManager.h"
119 #include "nsXULPrototypeCache.h"
120 #include "nsXULTooltipListener.h"
121 #include "xpcpublic.h"
123 using namespace mozilla;
124 using namespace mozilla::dom;
126 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
127 uint32_t nsXULPrototypeAttribute::gNumElements;
128 uint32_t nsXULPrototypeAttribute::gNumAttributes;
129 uint32_t nsXULPrototypeAttribute::gNumCacheTests;
130 uint32_t nsXULPrototypeAttribute::gNumCacheHits;
131 uint32_t nsXULPrototypeAttribute::gNumCacheSets;
132 uint32_t nsXULPrototypeAttribute::gNumCacheFills;
133 #endif
135 #define NS_DISPATCH_XUL_COMMAND (1 << 0)
137 //----------------------------------------------------------------------
138 // nsXULElement
141 nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
142 : nsStyledElement(std::move(aNodeInfo)) {
143 XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
146 nsXULElement::~nsXULElement() = default;
148 /* static */
149 nsXULElement* NS_NewBasicXULElement(
150 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
151 RefPtr<mozilla::dom::NodeInfo> nodeInfo(std::move(aNodeInfo));
152 auto* nim = nodeInfo->NodeInfoManager();
153 return new (nim) nsXULElement(nodeInfo.forget());
156 /* static */
157 nsXULElement* nsXULElement::Construct(
158 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
159 // NOTE: If you add elements here, you probably also want to change
160 // mozilla::dom::binding_detail::HTMLConstructor in BindingUtils.cpp to take
161 // them into account, otherwise you'll start getting "Illegal constructor"
162 // exceptions in chrome code.
163 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
164 if (nodeInfo->Equals(nsGkAtoms::resizer)) {
165 return NS_NewXULResizerElement(nodeInfo.forget());
168 if (nodeInfo->Equals(nsGkAtoms::label) ||
169 nodeInfo->Equals(nsGkAtoms::description)) {
170 auto* nim = nodeInfo->NodeInfoManager();
171 return new (nim) XULTextElement(nodeInfo.forget());
174 if (nodeInfo->Equals(nsGkAtoms::menupopup) ||
175 nodeInfo->Equals(nsGkAtoms::panel)) {
176 return NS_NewXULPopupElement(nodeInfo.forget());
179 if (nodeInfo->Equals(nsGkAtoms::tooltip)) {
180 return NS_NewXULTooltipElement(nodeInfo.forget());
183 if (nodeInfo->Equals(nsGkAtoms::iframe) ||
184 nodeInfo->Equals(nsGkAtoms::browser) ||
185 nodeInfo->Equals(nsGkAtoms::editor)) {
186 auto* nim = nodeInfo->NodeInfoManager();
187 return new (nim) XULFrameElement(nodeInfo.forget());
190 if (nodeInfo->Equals(nsGkAtoms::menubar)) {
191 auto* nim = nodeInfo->NodeInfoManager();
192 return new (nim) XULMenuBarElement(nodeInfo.forget());
195 if (nodeInfo->Equals(nsGkAtoms::menu) ||
196 nodeInfo->Equals(nsGkAtoms::menulist)) {
197 auto* nim = nodeInfo->NodeInfoManager();
198 return new (nim) XULMenuElement(nodeInfo.forget());
201 if (nodeInfo->Equals(nsGkAtoms::tree)) {
202 auto* nim = nodeInfo->NodeInfoManager();
203 return new (nim) XULTreeElement(nodeInfo.forget());
206 if (nodeInfo->Equals(nsGkAtoms::checkbox) ||
207 nodeInfo->Equals(nsGkAtoms::radio) ||
208 nodeInfo->Equals(nsGkAtoms::thumb) ||
209 nodeInfo->Equals(nsGkAtoms::button) ||
210 nodeInfo->Equals(nsGkAtoms::menuitem) ||
211 nodeInfo->Equals(nsGkAtoms::toolbarbutton) ||
212 nodeInfo->Equals(nsGkAtoms::toolbarpaletteitem) ||
213 nodeInfo->Equals(nsGkAtoms::scrollbarbutton)) {
214 auto* nim = nodeInfo->NodeInfoManager();
215 return new (nim) XULButtonElement(nodeInfo.forget());
218 return NS_NewBasicXULElement(nodeInfo.forget());
221 /* static */
222 already_AddRefed<nsXULElement> nsXULElement::CreateFromPrototype(
223 nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo* aNodeInfo,
224 bool aIsScriptable, bool aIsRoot) {
225 RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
226 nsCOMPtr<Element> baseElement;
227 NS_NewXULElement(getter_AddRefs(baseElement), ni.forget(),
228 dom::FROM_PARSER_NETWORK, aPrototype->mIsAtom);
230 if (baseElement) {
231 nsXULElement* element = FromNode(baseElement);
233 if (aPrototype->mHasIdAttribute) {
234 element->SetHasID();
236 if (aPrototype->mHasClassAttribute) {
237 element->SetMayHaveClass();
239 if (aPrototype->mHasStyleAttribute) {
240 element->SetMayHaveStyle();
243 element->MakeHeavyweight(aPrototype);
244 if (aIsScriptable) {
245 // Check each attribute on the prototype to see if we need to do
246 // any additional processing and hookup that would otherwise be
247 // done 'automagically' by SetAttr().
248 for (const auto& attribute : aPrototype->mAttributes) {
249 element->AddListenerForAttributeIfNeeded(attribute.mName);
253 return baseElement.forget().downcast<nsXULElement>();
256 return nullptr;
259 nsresult nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
260 Document* aDocument,
261 bool aIsScriptable, bool aIsRoot,
262 Element** aResult) {
263 // Create an nsXULElement from a prototype
264 MOZ_ASSERT(aPrototype != nullptr, "null ptr");
265 if (!aPrototype) return NS_ERROR_NULL_POINTER;
267 MOZ_ASSERT(aResult != nullptr, "null ptr");
268 if (!aResult) return NS_ERROR_NULL_POINTER;
270 RefPtr<mozilla::dom::NodeInfo> nodeInfo;
271 if (aDocument) {
272 mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo;
273 nodeInfo = aDocument->NodeInfoManager()->GetNodeInfo(
274 ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(), ELEMENT_NODE);
275 } else {
276 nodeInfo = aPrototype->mNodeInfo;
279 RefPtr<nsXULElement> element =
280 CreateFromPrototype(aPrototype, nodeInfo, aIsScriptable, aIsRoot);
281 element.forget(aResult);
283 return NS_OK;
286 nsresult NS_NewXULElement(Element** aResult,
287 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
288 FromParser aFromParser, nsAtom* aIsAtom,
289 mozilla::dom::CustomElementDefinition* aDefinition) {
290 RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
292 MOZ_ASSERT(nodeInfo, "need nodeinfo for non-proto Create");
294 NS_ASSERTION(
295 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
296 "Trying to create XUL elements that don't have the XUL namespace");
298 Document* doc = nodeInfo->GetDocument();
299 if (doc && !doc->AllowXULXBL()) {
300 return NS_ERROR_NOT_AVAILABLE;
303 return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser,
304 aIsAtom, aDefinition);
307 void NS_TrustedNewXULElement(
308 Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
309 RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
310 MOZ_ASSERT(ni, "need nodeinfo for non-proto Create");
312 // Create an nsXULElement with the specified namespace and tag.
313 NS_ADDREF(*aResult = nsXULElement::Construct(ni.forget()));
316 //----------------------------------------------------------------------
317 // nsISupports interface
319 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULElement, nsStyledElement)
321 NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement)
322 NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement)
324 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement)
325 NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
326 NS_INTERFACE_MAP_END_INHERITING(nsStyledElement)
328 //----------------------------------------------------------------------
329 // nsINode interface
331 nsresult nsXULElement::Clone(mozilla::dom::NodeInfo* aNodeInfo,
332 nsINode** aResult) const {
333 *aResult = nullptr;
335 RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
336 RefPtr<nsXULElement> element = Construct(ni.forget());
338 nsresult rv = const_cast<nsXULElement*>(this)->CopyInnerTo(
339 element, ReparseAttributes::No);
340 NS_ENSURE_SUCCESS(rv, rv);
342 // Note that we're _not_ copying mControllers.
344 element.forget(aResult);
345 return rv;
348 //----------------------------------------------------------------------
350 EventListenerManager* nsXULElement::GetEventListenerManagerForAttr(
351 nsAtom* aAttrName, bool* aDefer) {
352 // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc()
353 // here, override BindToTree for those classes and munge event
354 // listeners there?
355 Document* doc = OwnerDoc();
357 nsPIDOMWindowInner* window;
358 Element* root = doc->GetRootElement();
359 if ((!root || root == this) && (window = doc->GetInnerWindow())) {
360 nsCOMPtr<EventTarget> piTarget = do_QueryInterface(window);
362 *aDefer = false;
363 return piTarget->GetOrCreateListenerManager();
366 return nsStyledElement::GetEventListenerManagerForAttr(aAttrName, aDefer);
369 // returns true if the element is not a list
370 static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo) {
371 return !aNodeInfo->Equals(nsGkAtoms::tree) &&
372 !aNodeInfo->Equals(nsGkAtoms::richlistbox);
375 nsXULElement::XULFocusability nsXULElement::GetXULFocusability(
376 bool aWithMouse) {
377 #ifdef XP_MACOSX
378 // On Mac, mouse interactions only focus the element if it's a list,
379 // or if it's a remote target, since the remote target must handle
380 // the focus.
381 if (aWithMouse && IsNonList(mNodeInfo) &&
382 !EventStateManager::IsTopLevelRemoteTarget(this)) {
383 return XULFocusability::NeverFocusable();
385 #endif
387 XULFocusability result;
388 nsCOMPtr<nsIDOMXULControlElement> xulControl = AsXULControl();
389 if (xulControl) {
390 // A disabled element cannot be focused and is not part of the tab order
391 bool disabled;
392 xulControl->GetDisabled(&disabled);
393 if (disabled) {
394 return XULFocusability::NeverFocusable();
396 result.mDefaultFocusable = true;
398 if (Maybe<int32_t> attrVal = GetTabIndexAttrValue()) {
399 // The tabindex attribute was specified, so the element becomes
400 // focusable.
401 result.mDefaultFocusable = true;
402 result.mForcedFocusable.emplace(true);
403 result.mForcedTabIndexIfFocusable.emplace(attrVal.value());
405 if (xulControl && sTabFocusModelAppliesToXUL &&
406 !(sTabFocusModel & eTabFocus_formElementsMask) && IsNonList(mNodeInfo)) {
407 // By default, the tab focus model doesn't apply to xul element on any
408 // system but OS X. on OS X we're following it for UI elements (XUL) as
409 // sTabFocusModel is based on "Full Keyboard Access" system setting (see
410 // mac/nsILookAndFeel). both textboxes and list elements (i.e. trees and
411 // list) should always be focusable (textboxes are handled as html:input)
412 // For compatibility, we only do this for controls, otherwise elements
413 // like <browser> cannot take this focus.
414 result.mForcedTabIndexIfFocusable = Some(-1);
416 return result;
419 // XUL elements are not focusable unless explicitly opted-into it with
420 // -moz-user-focus: normal, or the tabindex attribute.
421 Focusable nsXULElement::IsFocusableWithoutStyle(bool aWithMouse) {
422 const auto focusability = GetXULFocusability(aWithMouse);
423 const bool focusable = focusability.mDefaultFocusable;
424 return {focusable,
425 focusable ? focusability.mForcedTabIndexIfFocusable.valueOr(-1) : -1};
428 bool nsXULElement::HasMenu() {
429 if (auto* button = XULButtonElement::FromNode(this)) {
430 return button->IsMenu();
432 return false;
435 void nsXULElement::OpenMenu(bool aOpenFlag) {
436 // Flush frames first. It's not clear why this is needed, see bug 1704670.
437 if (Document* doc = GetComposedDoc()) {
438 doc->FlushPendingNotifications(FlushType::Frames);
441 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
442 if (!pm) {
443 return;
446 if (aOpenFlag) {
447 // Nothing will happen if this element isn't a menu.
448 pm->ShowMenu(this, false);
449 } else {
450 // Nothing will happen if this element isn't a menu.
451 pm->HideMenu(this);
455 Result<bool, nsresult> nsXULElement::PerformAccesskey(bool aKeyCausesActivation,
456 bool aIsTrustedEvent) {
457 if (IsXULElement(nsGkAtoms::label)) {
458 nsAutoString control;
459 GetAttr(nsGkAtoms::control, control);
460 if (control.IsEmpty()) {
461 return Err(NS_ERROR_UNEXPECTED);
464 // XXXsmaug Should we use ShadowRoot::GetElementById in case
465 // element is in Shadow DOM?
466 RefPtr<Document> document = GetUncomposedDoc();
467 if (!document) {
468 return Err(NS_ERROR_UNEXPECTED);
471 RefPtr<Element> element = document->GetElementById(control);
472 if (!element) {
473 return Err(NS_ERROR_UNEXPECTED);
476 // XXXedgar, This is mainly for HTMLElement which doesn't do visible
477 // check in PerformAccesskey. We probably should always do visible
478 // check on HTMLElement even if the PerformAccesskey is not redirected from
479 // label XULelement per spec.
480 nsIFrame* frame = element->GetPrimaryFrame();
481 if (!frame || !frame->IsVisibleConsideringAncestors()) {
482 return Err(NS_ERROR_UNEXPECTED);
485 return element->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
488 nsIFrame* frame = GetPrimaryFrame();
489 if (!frame || !frame->IsVisibleConsideringAncestors()) {
490 return Err(NS_ERROR_UNEXPECTED);
493 bool focused = false;
494 // Define behavior for each type of XUL element.
495 if (!IsXULElement(nsGkAtoms::toolbarbutton)) {
496 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
497 RefPtr<Element> elementToFocus = this;
498 // for radio buttons, focus the radiogroup instead
499 if (IsXULElement(nsGkAtoms::radio)) {
500 if (nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem =
501 AsXULSelectControlItem()) {
502 bool disabled;
503 controlItem->GetDisabled(&disabled);
504 if (!disabled) {
505 controlItem->GetControl(getter_AddRefs(elementToFocus));
510 if (elementToFocus) {
511 fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY);
513 // Return true if the element became focused.
514 nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
515 focused = (window && window->GetFocusedElement() == elementToFocus);
520 if (aKeyCausesActivation && !IsXULElement(nsGkAtoms::menulist)) {
521 ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD,
522 aIsTrustedEvent);
523 return focused;
526 // If the accesskey won't cause the activation and the focus isn't changed,
527 // either. Return error so EventStateManager would try to find next element
528 // to handle the accesskey.
529 return focused ? Result<bool, nsresult>{focused} : Err(NS_ERROR_ABORT);
532 //----------------------------------------------------------------------
534 void nsXULElement::AddListenerForAttributeIfNeeded(nsAtom* aLocalName) {
535 // If appropriate, add a popup listener and/or compile the event
536 // handler. Called when we change the element's document, create a
537 // new element, change an attribute's value, etc.
538 // Eventlistenener-attributes are always in the null namespace.
539 if (aLocalName == nsGkAtoms::menu || aLocalName == nsGkAtoms::contextmenu ||
540 // XXXdwh popup and context are deprecated
541 aLocalName == nsGkAtoms::popup || aLocalName == nsGkAtoms::context) {
542 AddPopupListener(aLocalName);
544 if (nsContentUtils::IsEventAttributeName(aLocalName, EventNameType_XUL)) {
545 nsAutoString value;
546 GetAttr(aLocalName, value);
547 SetEventHandler(aLocalName, value, true);
551 void nsXULElement::AddListenerForAttributeIfNeeded(const nsAttrName& aName) {
552 if (aName.IsAtom()) {
553 AddListenerForAttributeIfNeeded(aName.Atom());
557 class XULInContentErrorReporter : public Runnable {
558 public:
559 explicit XULInContentErrorReporter(Document& aDocument)
560 : mozilla::Runnable("XULInContentErrorReporter"), mDocument(aDocument) {}
562 NS_IMETHOD Run() override {
563 mDocument->WarnOnceAbout(DeprecatedOperations::eImportXULIntoContent,
564 false);
565 return NS_OK;
568 private:
569 OwningNonNull<Document> mDocument;
572 static bool NeedTooltipSupport(const nsXULElement& aXULElement) {
573 if (aXULElement.NodeInfo()->Equals(nsGkAtoms::treechildren)) {
574 // treechildren always get tooltip support, since cropped tree cells show
575 // their full text in a tooltip.
576 return true;
579 return aXULElement.GetBoolAttr(nsGkAtoms::tooltip) ||
580 aXULElement.GetBoolAttr(nsGkAtoms::tooltiptext);
583 nsresult nsXULElement::BindToTree(BindContext& aContext, nsINode& aParent) {
584 nsresult rv = nsStyledElement::BindToTree(aContext, aParent);
585 NS_ENSURE_SUCCESS(rv, rv);
587 if (!IsInComposedDoc()) {
588 return rv;
591 Document& doc = aContext.OwnerDoc();
592 if (!IsInNativeAnonymousSubtree() && !doc.AllowXULXBL() &&
593 !doc.HasWarnedAbout(DeprecatedOperations::eImportXULIntoContent)) {
594 nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(doc));
597 #ifdef DEBUG
598 if (!doc.AllowXULXBL() && !doc.IsUnstyledDocument()) {
599 // To save CPU cycles and memory, we don't load xul.css for other elements
600 // except scrollbars.
602 // This assertion makes sure no other XUL element is used in a non-XUL
603 // document.
604 nsAtom* tag = NodeInfo()->NameAtom();
605 MOZ_ASSERT(tag == nsGkAtoms::scrollbar ||
606 tag == nsGkAtoms::scrollbarbutton ||
607 tag == nsGkAtoms::scrollcorner || tag == nsGkAtoms::slider ||
608 tag == nsGkAtoms::thumb || tag == nsGkAtoms::resizer,
609 "Unexpected XUL element in non-XUL doc");
611 #endif
613 // Within Bug 1492063 and its dependencies we started to apply a
614 // CSP to system privileged about pages. Since some about: pages
615 // are implemented in *.xul files we added this workaround to
616 // apply a CSP to them. To do so, we check the introduced custom
617 // attribute 'csp' on the root element.
618 if (doc.GetRootElement() == this) {
619 nsAutoString cspPolicyStr;
620 GetAttr(nsGkAtoms::csp, cspPolicyStr);
622 #ifdef DEBUG
624 nsCOMPtr<nsIContentSecurityPolicy> docCSP = doc.GetCsp();
625 uint32_t policyCount = 0;
626 if (docCSP) {
627 docCSP->GetPolicyCount(&policyCount);
629 MOZ_ASSERT(policyCount == 0, "how come we already have a policy?");
631 #endif
633 CSP_ApplyMetaCSPToDoc(doc, cspPolicyStr);
636 if (NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
637 // Create our XUL key listener and hook it up.
638 XULKeySetGlobalKeyListener::AttachKeyHandler(this);
641 RegUnRegAccessKey(true);
643 if (NeedTooltipSupport(*this)) {
644 AddTooltipSupport();
647 if (XULBroadcastManager::MayNeedListener(*this)) {
648 if (!doc.HasXULBroadcastManager()) {
649 doc.InitializeXULBroadcastManager();
651 XULBroadcastManager* broadcastManager = doc.GetXULBroadcastManager();
652 broadcastManager->AddListener(this);
654 return rv;
657 void nsXULElement::UnbindFromTree(UnbindContext& aContext) {
658 if (NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
659 XULKeySetGlobalKeyListener::DetachKeyHandler(this);
662 RegUnRegAccessKey(false);
664 if (NeedTooltipSupport(*this)) {
665 RemoveTooltipSupport();
668 Document* doc = GetComposedDoc();
669 if (doc && doc->HasXULBroadcastManager() &&
670 XULBroadcastManager::MayNeedListener(*this)) {
671 RefPtr<XULBroadcastManager> broadcastManager =
672 doc->GetXULBroadcastManager();
673 broadcastManager->RemoveListener(this);
676 // mControllers can own objects that are implemented
677 // in JavaScript (such as some implementations of
678 // nsIControllers. These objects prevent their global
679 // object's script object from being garbage collected,
680 // which means JS continues to hold an owning reference
681 // to the nsGlobalWindow, which owns the document,
682 // which owns this content. That's a cycle, so we break
683 // it here. (It might be better to break this by releasing
684 // mDocument in nsGlobalWindow::SetDocShell, but I'm not
685 // sure whether that would fix all possible cycles through
686 // mControllers.)
687 nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
688 if (slots) {
689 slots->mControllers = nullptr;
692 nsStyledElement::UnbindFromTree(aContext);
695 void nsXULElement::DoneAddingChildren(bool aHaveNotified) {
696 if (IsXULElement(nsGkAtoms::linkset)) {
697 Document* doc = GetComposedDoc();
698 if (doc) {
699 doc->OnL10nResourceContainerParsed();
704 void nsXULElement::RegUnRegAccessKey(bool aDoReg) {
705 // Don't try to register for unsupported elements
706 if (!SupportsAccessKey()) {
707 return;
710 nsStyledElement::RegUnRegAccessKey(aDoReg);
713 bool nsXULElement::SupportsAccessKey() const {
714 if (NodeInfo()->Equals(nsGkAtoms::label) && HasAttr(nsGkAtoms::control)) {
715 return true;
718 // XXX(ntim): check if description[value] or description[accesskey] are
719 // actually used, remove `value` from {Before/After}SetAttr if not the case
720 if (NodeInfo()->Equals(nsGkAtoms::description) && HasAttr(nsGkAtoms::value) &&
721 HasAttr(nsGkAtoms::control)) {
722 return true;
725 return IsAnyOfXULElements(nsGkAtoms::button, nsGkAtoms::toolbarbutton,
726 nsGkAtoms::checkbox, nsGkAtoms::tab,
727 nsGkAtoms::radio);
730 void nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
731 const nsAttrValue* aValue, bool aNotify) {
732 if (aNamespaceID == kNameSpaceID_None) {
733 if (aName == nsGkAtoms::accesskey || aName == nsGkAtoms::control ||
734 aName == nsGkAtoms::value) {
735 RegUnRegAccessKey(false);
736 } else if ((aName == nsGkAtoms::command || aName == nsGkAtoms::observes) &&
737 IsInUncomposedDoc()) {
738 // XXX sXBL/XBL2 issue! Owner or current document?
739 // XXX Why does this not also remove broadcast listeners if the
740 // "element" attribute was changed on an <observer>?
741 nsAutoString oldValue;
742 GetAttr(nsGkAtoms::observes, oldValue);
743 if (oldValue.IsEmpty()) {
744 GetAttr(nsGkAtoms::command, oldValue);
746 Document* doc = GetUncomposedDoc();
747 if (!oldValue.IsEmpty() && doc->HasXULBroadcastManager()) {
748 RefPtr<XULBroadcastManager> broadcastManager =
749 doc->GetXULBroadcastManager();
750 broadcastManager->RemoveListener(this);
752 #ifdef DEBUG
753 } else if (aName == nsGkAtoms::usercontextid) {
754 const nsAttrValue* oldValue = GetParsedAttr(aName);
755 if (oldValue && (!aValue || !aValue->Equals(*oldValue))) {
756 MOZ_ASSERT(false,
757 "Changing usercontextid doesn't really work properly.");
759 #endif
763 return nsStyledElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
766 void nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
767 const nsAttrValue* aValue,
768 const nsAttrValue* aOldValue,
769 nsIPrincipal* aSubjectPrincipal, bool aNotify) {
770 if (aNamespaceID == kNameSpaceID_None) {
771 if (aValue) {
772 AddListenerForAttributeIfNeeded(aName);
775 if (aName == nsGkAtoms::accesskey || aName == nsGkAtoms::control ||
776 aName == nsGkAtoms::value) {
777 RegUnRegAccessKey(true);
778 } else if (aName == nsGkAtoms::tooltip || aName == nsGkAtoms::tooltiptext) {
779 if (!!aValue != !!aOldValue && IsInComposedDoc() &&
780 !NodeInfo()->Equals(nsGkAtoms::treechildren)) {
781 if (aValue) {
782 AddTooltipSupport();
783 } else {
784 RemoveTooltipSupport();
788 Document* doc = GetComposedDoc();
789 if (doc && doc->HasXULBroadcastManager()) {
790 RefPtr<XULBroadcastManager> broadcastManager =
791 doc->GetXULBroadcastManager();
792 broadcastManager->AttributeChanged(this, aNamespaceID, aName);
794 if (doc && XULBroadcastManager::MayNeedListener(*this)) {
795 if (!doc->HasXULBroadcastManager()) {
796 doc->InitializeXULBroadcastManager();
798 XULBroadcastManager* broadcastManager = doc->GetXULBroadcastManager();
799 broadcastManager->AddListener(this);
802 // XXX need to check if they're changing an event handler: if
803 // so, then we need to unhook the old one. Or something.
806 return nsStyledElement::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
807 aSubjectPrincipal, aNotify);
810 void nsXULElement::AddTooltipSupport() {
811 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
812 if (!listener) {
813 return;
816 listener->AddTooltipSupport(this);
819 void nsXULElement::RemoveTooltipSupport() {
820 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
821 if (!listener) {
822 return;
825 listener->RemoveTooltipSupport(this);
828 bool nsXULElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
829 const nsAString& aValue,
830 nsIPrincipal* aMaybeScriptedPrincipal,
831 nsAttrValue& aResult) {
832 if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
833 return aResult.ParseIntValue(aValue);
836 // Parse into a nsAttrValue
837 if (!nsStyledElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
838 aMaybeScriptedPrincipal, aResult)) {
839 // Fall back to parsing as atom for short values
840 aResult.ParseStringOrAtom(aValue);
843 return true;
846 void nsXULElement::DestroyContent() {
847 nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
848 if (slots) {
849 slots->mControllers = nullptr;
852 nsStyledElement::DestroyContent();
855 #ifdef MOZ_DOM_LIST
856 void nsXULElement::List(FILE* out, int32_t aIndent) const {
857 nsCString prefix("XUL");
858 if (HasSlots()) {
859 prefix.Append('*');
861 prefix.Append(' ');
863 nsStyledElement::List(out, aIndent, prefix);
865 #endif
867 bool nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage) {
868 return (IsRootOfNativeAnonymousSubtree() &&
869 IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) &&
870 (aMessage == eMouseClick || aMessage == eMouseDoubleClick ||
871 aMessage == eXULCommand || aMessage == eContextMenu ||
872 aMessage == eDragStart || aMessage == eMouseAuxClick));
875 nsresult nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor,
876 nsAutoString& aCommand) {
877 // XXX sXBL/XBL2 issue! Owner or current document?
878 nsCOMPtr<Document> doc = GetUncomposedDoc();
879 NS_ENSURE_STATE(doc);
880 RefPtr<Element> commandElt = doc->GetElementById(aCommand);
881 if (commandElt) {
882 // Create a new command event to dispatch to the element
883 // pointed to by the command attribute. The new event's
884 // sourceEvent will be the original command event that we're
885 // handling.
886 RefPtr<Event> event = aVisitor.mDOMEvent;
887 uint16_t inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
888 int16_t button = 0;
889 while (event) {
890 NS_ENSURE_STATE(event->GetOriginalTarget() != commandElt);
891 RefPtr<XULCommandEvent> commandEvent = event->AsXULCommandEvent();
892 if (commandEvent) {
893 event = commandEvent->GetSourceEvent();
894 inputSource = commandEvent->InputSource();
895 button = commandEvent->Button();
896 } else {
897 event = nullptr;
900 WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent();
901 nsContentUtils::DispatchXULCommand(
902 commandElt, orig->IsTrusted(), MOZ_KnownLive(aVisitor.mDOMEvent),
903 nullptr, orig->IsControl(), orig->IsAlt(), orig->IsShift(),
904 orig->IsMeta(), inputSource, button);
905 } else {
906 NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
908 return NS_OK;
911 void nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
912 aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
913 if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) {
914 // Don't propagate these events from native anonymous scrollbar.
915 aVisitor.mCanHandle = true;
916 aVisitor.SetParentTarget(nullptr, false);
917 return;
919 if (aVisitor.mEvent->mMessage == eXULCommand &&
920 aVisitor.mEvent->mClass == eInputEventClass &&
921 aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this) &&
922 !IsXULElement(nsGkAtoms::command)) {
923 // Check that we really have an xul command event. That will be handled
924 // in a special way.
925 // See if we have a command elt. If so, we execute on the command
926 // instead of on our content element.
927 if (aVisitor.mDOMEvent && aVisitor.mDOMEvent->AsXULCommandEvent() &&
928 HasNonEmptyAttr(nsGkAtoms::command)) {
929 // Stop building the event target chain for the original event.
930 // We don't want it to propagate to any DOM nodes.
931 aVisitor.mCanHandle = false;
932 aVisitor.mAutomaticChromeDispatch = false;
933 // Dispatch XUL command in PreHandleEvent to prevent it breaks event
934 // target chain creation
935 aVisitor.mWantsPreHandleEvent = true;
936 aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND;
937 return;
941 nsStyledElement::GetEventTargetParent(aVisitor);
944 nsresult nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor) {
945 if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) {
946 nsAutoString command;
947 GetAttr(nsGkAtoms::command, command);
948 MOZ_ASSERT(!command.IsEmpty());
949 return DispatchXULCommand(aVisitor, command);
951 return nsStyledElement::PreHandleEvent(aVisitor);
954 //----------------------------------------------------------------------
955 // Implementation methods
957 NS_IMETHODIMP_(bool)
958 nsXULElement::IsAttributeMapped(const nsAtom* aAttribute) const {
959 return false;
962 nsIControllers* nsXULElement::GetControllers(ErrorResult& rv) {
963 if (!Controllers()) {
964 nsExtendedDOMSlots* slots = ExtendedDOMSlots();
966 slots->mControllers = new nsXULControllers();
969 return Controllers();
972 void nsXULElement::Click(CallerType aCallerType) {
973 ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
974 aCallerType == CallerType::System);
977 void nsXULElement::ClickWithInputSource(uint16_t aInputSource,
978 bool aIsTrustedEvent) {
979 if (BoolAttrIsTrue(nsGkAtoms::disabled)) return;
981 nsCOMPtr<Document> doc = GetComposedDoc(); // Strong just in case
982 if (doc) {
983 RefPtr<nsPresContext> context = doc->GetPresContext();
984 if (context) {
985 // strong ref to PresContext so events don't destroy it
987 WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown, nullptr,
988 WidgetMouseEvent::eReal);
989 WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp, nullptr,
990 WidgetMouseEvent::eReal);
991 // This helps to avoid commands being dispatched from
992 // XULButtonElement::PostHandleEventForMenu.
993 eventUp.mFlags.mMultipleActionsPrevented = true;
994 WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr,
995 WidgetMouseEvent::eReal);
996 eventDown.mInputSource = eventUp.mInputSource = eventClick.mInputSource =
997 aInputSource;
999 // send mouse down
1000 nsEventStatus status = nsEventStatus_eIgnore;
1001 EventDispatcher::Dispatch(this, context, &eventDown, nullptr, &status);
1003 // send mouse up
1004 status = nsEventStatus_eIgnore; // reset status
1005 EventDispatcher::Dispatch(this, context, &eventUp, nullptr, &status);
1007 // send mouse click
1008 status = nsEventStatus_eIgnore; // reset status
1009 EventDispatcher::Dispatch(this, context, &eventClick, nullptr, &status);
1011 // If the click has been prevented, lets skip the command call
1012 // this is how a physical click works
1013 if (status == nsEventStatus_eConsumeNoDefault) {
1014 return;
1019 // oncommand is fired when an element is clicked...
1020 DoCommand();
1023 void nsXULElement::DoCommand() {
1024 nsCOMPtr<Document> doc = GetComposedDoc(); // strong just in case
1025 if (doc) {
1026 RefPtr<nsXULElement> self = this;
1027 nsContentUtils::DispatchXULCommand(self, true);
1031 nsresult nsXULElement::AddPopupListener(nsAtom* aName) {
1032 // Add a popup listener to the element
1033 bool isContext =
1034 (aName == nsGkAtoms::context || aName == nsGkAtoms::contextmenu);
1035 uint32_t listenerFlag = isContext ? XUL_ELEMENT_HAS_CONTENTMENU_LISTENER
1036 : XUL_ELEMENT_HAS_POPUP_LISTENER;
1038 if (HasFlag(listenerFlag)) {
1039 return NS_OK;
1042 nsCOMPtr<nsIDOMEventListener> listener =
1043 new nsXULPopupListener(this, isContext);
1045 // Add the popup as a listener on this element.
1046 EventListenerManager* manager = GetOrCreateListenerManager();
1047 SetFlags(listenerFlag);
1049 if (isContext) {
1050 manager->AddEventListenerByType(listener, u"contextmenu"_ns,
1051 TrustedEventsAtSystemGroupBubble());
1052 } else {
1053 manager->AddEventListenerByType(listener, u"mousedown"_ns,
1054 TrustedEventsAtSystemGroupBubble());
1056 return NS_OK;
1059 //----------------------------------------------------------------------
1061 nsresult nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) {
1062 if (!aPrototype) {
1063 return NS_OK;
1066 size_t i;
1067 nsresult rv;
1068 for (i = 0; i < aPrototype->mAttributes.Length(); ++i) {
1069 nsXULPrototypeAttribute* protoattr = &aPrototype->mAttributes[i];
1070 nsAttrValue attrValue;
1072 // Style rules need to be cloned.
1073 if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) {
1074 DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue();
1075 RefPtr<DeclarationBlock> declClone = decl->Clone();
1077 nsString stringValue;
1078 protoattr->mValue.ToString(stringValue);
1080 attrValue.SetTo(declClone.forget(), &stringValue);
1081 } else {
1082 attrValue.SetTo(protoattr->mValue);
1085 bool oldValueSet;
1086 // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName
1087 if (protoattr->mName.IsAtom()) {
1088 rv = mAttrs.SetAndSwapAttr(protoattr->mName.Atom(), attrValue,
1089 &oldValueSet);
1090 } else {
1091 rv = mAttrs.SetAndSwapAttr(protoattr->mName.NodeInfo(), attrValue,
1092 &oldValueSet);
1094 NS_ENSURE_SUCCESS(rv, rv);
1096 return NS_OK;
1099 bool nsXULElement::BoolAttrIsTrue(nsAtom* aName) const {
1100 const nsAttrValue* attr = GetAttrInfo(kNameSpaceID_None, aName).mValue;
1102 return attr && attr->Type() == nsAttrValue::eAtom &&
1103 attr->GetAtomValue() == nsGkAtoms::_true;
1106 bool nsXULElement::IsEventAttributeNameInternal(nsAtom* aName) {
1107 return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL);
1110 JSObject* nsXULElement::WrapNode(JSContext* aCx,
1111 JS::Handle<JSObject*> aGivenProto) {
1112 return dom::XULElement_Binding::Wrap(aCx, this, aGivenProto);
1115 bool nsXULElement::IsInteractiveHTMLContent() const {
1116 return IsXULElement(nsGkAtoms::menupopup) ||
1117 Element::IsInteractiveHTMLContent();
1120 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
1122 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode)
1123 if (tmp->mType == nsXULPrototypeNode::eType_Element) {
1124 static_cast<nsXULPrototypeElement*>(tmp)->Unlink();
1126 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1127 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode)
1128 if (tmp->mType == nsXULPrototypeNode::eType_Element) {
1129 nsXULPrototypeElement* elem = static_cast<nsXULPrototypeElement*>(tmp);
1130 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo");
1131 cb.NoteNativeChild(elem->mNodeInfo,
1132 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1133 size_t i;
1134 for (i = 0; i < elem->mAttributes.Length(); ++i) {
1135 const nsAttrName& name = elem->mAttributes[i].mName;
1136 if (!name.IsAtom()) {
1137 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1138 "mAttributes[i].mName.NodeInfo()");
1139 cb.NoteNativeChild(name.NodeInfo(),
1140 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1143 ImplCycleCollectionTraverse(cb, elem->mChildren, "mChildren");
1145 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1146 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode)
1147 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1149 //----------------------------------------------------------------------
1151 // nsXULPrototypeAttribute
1154 nsXULPrototypeAttribute::~nsXULPrototypeAttribute() {
1155 MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
1158 //----------------------------------------------------------------------
1160 // nsXULPrototypeElement
1163 nsresult nsXULPrototypeElement::Serialize(
1164 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1165 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1166 nsresult rv;
1168 // Write basic prototype data
1169 rv = aStream->Write32(mType);
1171 // Write Node Info
1172 int32_t index = aNodeInfos->IndexOf(mNodeInfo);
1173 NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
1174 nsresult tmp = aStream->Write32(index);
1175 if (NS_FAILED(tmp)) {
1176 rv = tmp;
1179 // Write Attributes
1180 tmp = aStream->Write32(mAttributes.Length());
1181 if (NS_FAILED(tmp)) {
1182 rv = tmp;
1185 nsAutoString attributeValue;
1186 size_t i;
1187 for (i = 0; i < mAttributes.Length(); ++i) {
1188 RefPtr<mozilla::dom::NodeInfo> ni;
1189 if (mAttributes[i].mName.IsAtom()) {
1190 ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(
1191 mAttributes[i].mName.Atom(), nullptr, kNameSpaceID_None,
1192 nsINode::ATTRIBUTE_NODE);
1193 NS_ASSERTION(ni, "the nodeinfo should already exist");
1194 } else {
1195 ni = mAttributes[i].mName.NodeInfo();
1198 index = aNodeInfos->IndexOf(ni);
1199 NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
1200 tmp = aStream->Write32(index);
1201 if (NS_FAILED(tmp)) {
1202 rv = tmp;
1205 mAttributes[i].mValue.ToString(attributeValue);
1206 tmp = aStream->WriteWStringZ(attributeValue.get());
1207 if (NS_FAILED(tmp)) {
1208 rv = tmp;
1212 // Now write children
1213 tmp = aStream->Write32(uint32_t(mChildren.Length()));
1214 if (NS_FAILED(tmp)) {
1215 rv = tmp;
1217 for (i = 0; i < mChildren.Length(); i++) {
1218 nsXULPrototypeNode* child = mChildren[i].get();
1219 switch (child->mType) {
1220 case eType_Element:
1221 case eType_Text:
1222 case eType_PI:
1223 tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos);
1224 if (NS_FAILED(tmp)) {
1225 rv = tmp;
1227 break;
1228 case eType_Script:
1229 tmp = aStream->Write32(child->mType);
1230 if (NS_FAILED(tmp)) {
1231 rv = tmp;
1233 nsXULPrototypeScript* script =
1234 static_cast<nsXULPrototypeScript*>(child);
1236 tmp = aStream->Write8(script->mOutOfLine);
1237 if (NS_FAILED(tmp)) {
1238 rv = tmp;
1240 if (!script->mOutOfLine) {
1241 tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos);
1242 if (NS_FAILED(tmp)) {
1243 rv = tmp;
1245 } else {
1246 tmp = aStream->WriteCompoundObject(script->mSrcURI,
1247 NS_GET_IID(nsIURI), true);
1248 if (NS_FAILED(tmp)) {
1249 rv = tmp;
1252 if (script->HasStencil()) {
1253 // This may return NS_OK without muxing script->mSrcURI's
1254 // data into the cache file, in the case where that
1255 // muxed document is already there (written by a prior
1256 // session, or by an earlier cache episode during this
1257 // session).
1258 tmp = script->SerializeOutOfLine(aStream, aProtoDoc);
1259 if (NS_FAILED(tmp)) {
1260 rv = tmp;
1264 break;
1268 return rv;
1271 nsresult nsXULPrototypeElement::Deserialize(
1272 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1273 nsIURI* aDocumentURI,
1274 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1275 MOZ_ASSERT(aNodeInfos, "missing nodeinfo array");
1277 // Read Node Info
1278 uint32_t number = 0;
1279 nsresult rv = aStream->Read32(&number);
1280 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1281 mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr);
1282 if (!mNodeInfo) {
1283 return NS_ERROR_UNEXPECTED;
1286 if (mNodeInfo->Equals(nsGkAtoms::parsererror) &&
1287 mNodeInfo->NamespaceEquals(
1288 nsDependentAtomString(nsGkAtoms::nsuri_parsererror))) {
1289 return NS_ERROR_UNEXPECTED;
1292 // Read Attributes
1293 rv = aStream->Read32(&number);
1294 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1295 int32_t attributes = int32_t(number);
1297 if (attributes > 0) {
1298 mAttributes.AppendElements(attributes);
1300 nsAutoString attributeValue;
1301 for (size_t i = 0; i < mAttributes.Length(); ++i) {
1302 rv = aStream->Read32(&number);
1303 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1304 mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr);
1305 if (!ni) {
1306 return NS_ERROR_UNEXPECTED;
1309 mAttributes[i].mName.SetTo(ni);
1311 rv = aStream->ReadString(attributeValue);
1312 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1313 rv = SetAttrAt(i, attributeValue, aDocumentURI);
1314 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1318 rv = aStream->Read32(&number);
1319 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1320 uint32_t numChildren = int32_t(number);
1322 if (numChildren > 0) {
1323 if (!mChildren.SetCapacity(numChildren, fallible)) {
1324 return NS_ERROR_OUT_OF_MEMORY;
1327 for (uint32_t i = 0; i < numChildren; i++) {
1328 rv = aStream->Read32(&number);
1329 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1330 Type childType = (Type)number;
1332 RefPtr<nsXULPrototypeNode> child;
1334 switch (childType) {
1335 case eType_Element:
1336 child = new nsXULPrototypeElement();
1337 rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, aNodeInfos);
1338 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1339 break;
1340 case eType_Text:
1341 child = new nsXULPrototypeText();
1342 rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, aNodeInfos);
1343 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1344 break;
1345 case eType_PI:
1346 child = new nsXULPrototypePI();
1347 rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, aNodeInfos);
1348 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1349 break;
1350 case eType_Script: {
1351 // language version/options obtained during deserialization.
1352 RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0);
1354 rv = aStream->ReadBoolean(&script->mOutOfLine);
1355 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1356 if (!script->mOutOfLine) {
1357 rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
1358 aNodeInfos);
1359 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1360 } else {
1361 nsCOMPtr<nsISupports> supports;
1362 rv = aStream->ReadObject(true, getter_AddRefs(supports));
1363 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1364 script->mSrcURI = do_QueryInterface(supports);
1366 rv = script->DeserializeOutOfLine(aStream, aProtoDoc);
1367 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1370 child = std::move(script);
1371 break;
1373 default:
1374 MOZ_ASSERT(false, "Unexpected child type!");
1375 return NS_ERROR_UNEXPECTED;
1378 MOZ_ASSERT(child, "Don't append null to mChildren");
1379 MOZ_ASSERT(child->mType == childType);
1380 mChildren.AppendElement(child);
1382 // Oh dear. Something failed during the deserialization.
1383 // We don't know what. But likely consequences of failed
1384 // deserializations included calls to |AbortCaching| which
1385 // shuts down the cache and closes our streams.
1386 // If that happens, next time through this loop, we die a messy
1387 // death. So, let's just fail now, and propagate that failure
1388 // upward so that the ChromeProtocolHandler knows it can't use
1389 // a cached chrome channel for this.
1390 if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1394 return rv;
1397 nsresult nsXULPrototypeElement::SetAttrAt(uint32_t aPos,
1398 const nsAString& aValue,
1399 nsIURI* aDocumentURI) {
1400 MOZ_ASSERT(aPos < mAttributes.Length(), "out-of-bounds");
1402 // WARNING!!
1403 // This code is largely duplicated in nsXULElement::SetAttr.
1404 // Any changes should be made to both functions.
1406 if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
1407 if (mNodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
1408 mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
1409 // We still care about the is attribute set on HTML elements.
1410 mAttributes[aPos].mValue.ParseAtom(aValue);
1411 mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
1413 return NS_OK;
1416 mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
1418 return NS_OK;
1421 if (mAttributes[aPos].mName.Equals(nsGkAtoms::id) && !aValue.IsEmpty()) {
1422 mHasIdAttribute = true;
1423 // Store id as atom.
1424 // id="" means that the element has no id. Not that it has
1425 // emptystring as id.
1426 mAttributes[aPos].mValue.ParseAtom(aValue);
1428 return NS_OK;
1429 } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
1430 // Store is as atom.
1431 mAttributes[aPos].mValue.ParseAtom(aValue);
1432 mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
1434 return NS_OK;
1435 } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) {
1436 mHasClassAttribute = true;
1437 // Compute the element's class list
1438 mAttributes[aPos].mValue.ParseAtomArray(aValue);
1440 return NS_OK;
1441 } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) {
1442 mHasStyleAttribute = true;
1443 // Parse the element's 'style' attribute
1445 // This is basically duplicating what nsINode::NodePrincipal() does
1446 nsIPrincipal* principal = mNodeInfo->NodeInfoManager()->DocumentPrincipal();
1447 // XXX Get correct Base URI (need GetBaseURI on *prototype* element)
1448 // TODO: If we implement Content Security Policy for chrome documents
1449 // as has been discussed, the CSP should be checked here to see if
1450 // inline styles are allowed to be applied.
1451 // XXX No specific specs talk about xul and referrer policy, pass Unset
1452 auto referrerInfo =
1453 MakeRefPtr<ReferrerInfo>(aDocumentURI, ReferrerPolicy::_empty);
1454 auto data = MakeRefPtr<URLExtraData>(aDocumentURI, referrerInfo, principal);
1455 RefPtr<DeclarationBlock> declaration = DeclarationBlock::FromCssText(
1456 aValue, data, eCompatibility_FullStandards, nullptr,
1457 StyleCssRuleType::Style);
1458 if (declaration) {
1459 mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue);
1461 return NS_OK;
1463 // Don't abort if parsing failed, it could just be malformed css.
1464 } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::tabindex)) {
1465 mAttributes[aPos].mValue.ParseIntValue(aValue);
1467 return NS_OK;
1470 mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
1472 return NS_OK;
1475 void nsXULPrototypeElement::Unlink() {
1476 mAttributes.Clear();
1477 mChildren.Clear();
1480 //----------------------------------------------------------------------
1482 // nsXULPrototypeScript
1485 nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo)
1486 : nsXULPrototypeNode(eType_Script),
1487 mLineNo(aLineNo),
1488 mSrcLoading(false),
1489 mOutOfLine(true),
1490 mSrcLoadWaiters(nullptr),
1491 mStencil(nullptr) {}
1493 static nsresult WriteStencil(nsIObjectOutputStream* aStream, JSContext* aCx,
1494 JS::Stencil* aStencil) {
1495 JS::TranscodeBuffer buffer;
1496 JS::TranscodeResult code;
1497 code = JS::EncodeStencil(aCx, aStencil, buffer);
1499 if (code != JS::TranscodeResult::Ok) {
1500 if (code == JS::TranscodeResult::Throw) {
1501 JS_ClearPendingException(aCx);
1502 return NS_ERROR_OUT_OF_MEMORY;
1505 MOZ_ASSERT(IsTranscodeFailureResult(code));
1506 return NS_ERROR_FAILURE;
1509 size_t size = buffer.length();
1510 if (size > UINT32_MAX) {
1511 return NS_ERROR_FAILURE;
1513 nsresult rv = aStream->Write32(size);
1514 if (NS_SUCCEEDED(rv)) {
1515 // Ideally we could just pass "buffer" here. See bug 1566574.
1516 rv = aStream->WriteBytes(Span(buffer.begin(), size));
1519 return rv;
1522 static nsresult ReadStencil(nsIObjectInputStream* aStream, JSContext* aCx,
1523 const JS::ReadOnlyDecodeOptions& aOptions,
1524 JS::Stencil** aStencilOut) {
1525 // We don't serialize mutedError-ness of scripts, which is fine as long as
1526 // we only serialize system and XUL-y things. We can detect this by checking
1527 // where the caller wants us to deserialize.
1529 // CompilationScope() could theoretically GC, so get that out of the way
1530 // before comparing to the cx global.
1531 JSObject* loaderGlobal = xpc::CompilationScope();
1532 MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(aCx) ||
1533 JS::CurrentGlobalOrNull(aCx) == loaderGlobal);
1535 uint32_t size;
1536 nsresult rv = aStream->Read32(&size);
1537 if (NS_FAILED(rv)) {
1538 return rv;
1541 char* data;
1542 rv = aStream->ReadBytes(size, &data);
1543 if (NS_FAILED(rv)) {
1544 return rv;
1547 // The decoded stencil shouldn't borrow from the XDR buffer.
1548 MOZ_ASSERT(!aOptions.borrowBuffer);
1549 auto cleanupData = MakeScopeExit([&]() { free(data); });
1551 JS::TranscodeRange range(reinterpret_cast<uint8_t*>(data), size);
1554 JS::TranscodeResult code;
1555 RefPtr<JS::Stencil> stencil;
1556 code = JS::DecodeStencil(aCx, aOptions, range, getter_AddRefs(stencil));
1557 if (code != JS::TranscodeResult::Ok) {
1558 if (code == JS::TranscodeResult::Throw) {
1559 JS_ClearPendingException(aCx);
1560 return NS_ERROR_OUT_OF_MEMORY;
1563 MOZ_ASSERT(IsTranscodeFailureResult(code));
1564 return NS_ERROR_FAILURE;
1567 stencil.forget(aStencilOut);
1570 return rv;
1573 void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions& aOptions,
1574 const char* aFilename,
1575 uint32_t aLineNo) {
1576 // NOTE: This method shouldn't change any field which also exists in
1577 // JS::InstantiateOptions. If such field is added,
1578 // nsXULPrototypeScript::InstantiateScript should also call this method.
1580 // If the script was inline, tell the JS parser to save source for
1581 // Function.prototype.toSource(). If it's out of line, we retrieve the
1582 // source from the files on demand.
1583 aOptions.setSourceIsLazy(mOutOfLine);
1585 aOptions.setIntroductionType(mOutOfLine ? "srcScript" : "inlineScript")
1586 .setFileAndLine(aFilename, mOutOfLine ? 1 : aLineNo);
1589 nsresult nsXULPrototypeScript::Serialize(
1590 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1591 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1592 NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED);
1594 AutoJSAPI jsapi;
1595 if (!jsapi.Init(xpc::CompilationScope())) {
1596 return NS_ERROR_UNEXPECTED;
1599 NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mStencil,
1600 "script source still loading when serializing?!");
1601 if (!mStencil) return NS_ERROR_FAILURE;
1603 // Write basic prototype data
1604 nsresult rv;
1605 rv = aStream->Write32(mLineNo);
1606 if (NS_FAILED(rv)) return rv;
1608 JSContext* cx = jsapi.cx();
1609 MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
1611 return WriteStencil(aStream, cx, mStencil);
1614 nsresult nsXULPrototypeScript::SerializeOutOfLine(
1615 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc) {
1616 if (!mSrcURI->SchemeIs("chrome"))
1617 // Don't cache scripts that don't come from chrome uris.
1618 return NS_ERROR_NOT_IMPLEMENTED;
1620 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1621 if (!cache) return NS_ERROR_OUT_OF_MEMORY;
1623 NS_ASSERTION(cache->IsEnabled(),
1624 "writing to the cache file, but the XUL cache is off?");
1625 bool exists;
1626 cache->HasScript(mSrcURI, &exists);
1628 /* return will be NS_OK from GetAsciiSpec.
1629 * that makes no sense.
1630 * nor does returning NS_OK from HasMuxedDocument.
1631 * XXX return something meaningful.
1633 if (exists) return NS_OK;
1635 nsCOMPtr<nsIObjectOutputStream> oos;
1636 nsresult rv = cache->GetScriptOutputStream(mSrcURI, getter_AddRefs(oos));
1637 NS_ENSURE_SUCCESS(rv, rv);
1639 nsresult tmp = Serialize(oos, aProtoDoc, nullptr);
1640 if (NS_FAILED(tmp)) {
1641 rv = tmp;
1643 tmp = cache->FinishScriptOutputStream(mSrcURI);
1644 if (NS_FAILED(tmp)) {
1645 rv = tmp;
1648 if (NS_FAILED(rv)) cache->AbortCaching();
1649 return rv;
1652 nsresult nsXULPrototypeScript::Deserialize(
1653 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1654 nsIURI* aDocumentURI,
1655 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1656 nsresult rv;
1657 NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mStencil,
1658 "prototype script not well-initialized when deserializing?!");
1660 // Read basic prototype data
1661 rv = aStream->Read32(&mLineNo);
1662 if (NS_FAILED(rv)) return rv;
1664 AutoJSAPI jsapi;
1665 if (!jsapi.Init(xpc::CompilationScope())) {
1666 return NS_ERROR_UNEXPECTED;
1668 JSContext* cx = jsapi.cx();
1670 JS::DecodeOptions options;
1671 RefPtr<JS::Stencil> newStencil;
1672 rv = ReadStencil(aStream, cx, options, getter_AddRefs(newStencil));
1673 NS_ENSURE_SUCCESS(rv, rv);
1674 Set(newStencil);
1675 return NS_OK;
1678 nsresult nsXULPrototypeScript::DeserializeOutOfLine(
1679 nsIObjectInputStream* aInput, nsXULPrototypeDocument* aProtoDoc) {
1680 // Keep track of failure via rv, so we can
1681 // AbortCaching if things look bad.
1682 nsresult rv = NS_OK;
1683 nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1685 nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
1686 if (cache) {
1687 bool useXULCache = true;
1688 if (mSrcURI) {
1689 // NB: we must check the XUL script cache early, to avoid
1690 // multiple deserialization attempts for a given script.
1691 // Note that PrototypeDocumentContentSink::LoadScript
1692 // checks the XUL script cache too, in order to handle the
1693 // serialization case.
1695 // We need do this only for <script src='strres.js'> and the
1696 // like, i.e., out-of-line scripts that are included by several
1697 // different XUL documents stored in the cache file.
1698 useXULCache = cache->IsEnabled();
1700 if (useXULCache) {
1701 RefPtr<JS::Stencil> newStencil = cache->GetStencil(mSrcURI);
1702 if (newStencil) {
1703 Set(newStencil);
1708 if (!mStencil) {
1709 if (mSrcURI) {
1710 rv = cache->GetScriptInputStream(mSrcURI, getter_AddRefs(objectInput));
1712 // If !mSrcURI, we have an inline script. We shouldn't have
1713 // to do anything else in that case, I think.
1715 // We do reflect errors into rv, but our caller may want to
1716 // ignore our return value, because mStencil will be null
1717 // after any error, and that suffices to cause the script to
1718 // be reloaded (from the src= URI, if any) and recompiled.
1719 // We're better off slow-loading than bailing out due to a
1720 // error.
1721 if (NS_SUCCEEDED(rv))
1722 rv = Deserialize(objectInput, aProtoDoc, nullptr, nullptr);
1724 if (NS_SUCCEEDED(rv)) {
1725 if (useXULCache && mSrcURI && mSrcURI->SchemeIs("chrome")) {
1726 cache->PutStencil(mSrcURI, GetStencil());
1728 cache->FinishScriptInputStream(mSrcURI);
1729 } else {
1730 // If mSrcURI is not in the cache,
1731 // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
1732 // update the cache file to hold a serialization of
1733 // this script, once it has finished loading.
1734 if (rv != NS_ERROR_NOT_AVAILABLE) cache->AbortCaching();
1738 return rv;
1741 #ifdef DEBUG
1742 static void CheckErrorsAndWarnings(JS::FrontendContext* aFc,
1743 const JS::ReadOnlyCompileOptions& aOptions) {
1744 if (JS::HadFrontendErrors(aFc)) {
1745 const JSErrorReport* report = JS::GetFrontendErrorReport(aFc, aOptions);
1746 if (report) {
1747 const char* message = "<unknown>";
1748 const char* filename = "<unknown>";
1750 if (report->message().c_str()) {
1751 message = report->message().c_str();
1753 if (report->filename.c_str()) {
1754 filename = report->filename.c_str();
1757 NS_WARNING(
1758 nsPrintfCString(
1759 "Had compilation error in ScriptCompileTask: %s at %s:%u:%u",
1760 message, filename, report->lineno,
1761 report->column.oneOriginValue())
1762 .get());
1765 if (JS::HadFrontendOverRecursed(aFc)) {
1766 NS_WARNING("Had over recursed in ScriptCompileTask");
1769 if (JS::HadFrontendOutOfMemory(aFc)) {
1770 NS_WARNING("Had out of memory in ScriptCompileTask");
1773 if (JS::HadFrontendAllocationOverflow(aFc)) {
1774 NS_WARNING("Had allocation overflow in ScriptCompileTask");
1778 size_t count = JS::GetFrontendWarningCount(aFc);
1779 for (size_t i = 0; i < count; i++) {
1780 const JSErrorReport* report = JS::GetFrontendWarningAt(aFc, i, aOptions);
1782 const char* message = "<unknown>";
1783 const char* filename = "<unknown>";
1785 if (report->message().c_str()) {
1786 message = report->message().c_str();
1788 if (report->filename.c_str()) {
1789 filename = report->filename.c_str();
1792 NS_WARNING(
1793 nsPrintfCString(
1794 "Had compilation warning in ScriptCompileTask: %s at %s:%u:%u",
1795 message, filename, report->lineno, report->column.oneOriginValue())
1796 .get());
1799 #endif
1801 class ScriptCompileTask final : public Task {
1802 public:
1803 explicit ScriptCompileTask(UniquePtr<Utf8Unit[], JS::FreePolicy>&& aText,
1804 size_t aTextLength)
1805 : Task(Kind::OffMainThreadOnly, EventQueuePriority::Normal),
1806 mOptions(JS::OwningCompileOptions::ForFrontendContext()),
1807 mText(std::move(aText)),
1808 mTextLength(aTextLength) {}
1810 ~ScriptCompileTask() {
1811 if (mFrontendContext) {
1812 JS::DestroyFrontendContext(mFrontendContext);
1816 nsresult Init(JS::CompileOptions& aOptions) {
1817 mFrontendContext = JS::NewFrontendContext();
1818 if (!mFrontendContext) {
1819 return NS_ERROR_FAILURE;
1822 if (!mOptions.copy(mFrontendContext, aOptions)) {
1823 return NS_ERROR_FAILURE;
1826 return NS_OK;
1829 private:
1830 void Compile() {
1831 // NOTE: The stack limit must be set from the same thread that compiles.
1832 size_t stackSize = TaskController::GetThreadStackSize();
1833 JS::SetNativeStackQuota(mFrontendContext,
1834 JS::ThreadStackQuotaForSize(stackSize));
1836 JS::SourceText<Utf8Unit> srcBuf;
1837 if (NS_WARN_IF(!srcBuf.init(mFrontendContext, mText.get(), mTextLength,
1838 JS::SourceOwnership::Borrowed))) {
1839 return;
1842 JS::CompilationStorage compileStorage;
1843 mStencil = JS::CompileGlobalScriptToStencil(mFrontendContext, mOptions,
1844 srcBuf, compileStorage);
1845 #ifdef DEBUG
1846 // Chrome-privileged code shouldn't have any compilation error.
1847 CheckErrorsAndWarnings(mFrontendContext, mOptions);
1848 MOZ_ASSERT(mStencil);
1849 #endif
1852 public:
1853 TaskResult Run() override {
1854 Compile();
1855 return TaskResult::Complete;
1858 already_AddRefed<JS::Stencil> StealStencil() { return mStencil.forget(); }
1860 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
1861 bool GetName(nsACString& aName) override {
1862 aName.AssignLiteral("ScriptCompileTask");
1863 return true;
1865 #endif
1867 private:
1868 // Owning-pointer for the context associated with the script compilation.
1870 // The context is allocated on main thread in Init method, and is freed on
1871 // any thread in the destructor.
1872 JS::FrontendContext* mFrontendContext = nullptr;
1874 JS::OwningCompileOptions mOptions;
1876 RefPtr<JS::Stencil> mStencil;
1878 // The source text for this compilation.
1879 UniquePtr<Utf8Unit[], JS::FreePolicy> mText;
1880 size_t mTextLength;
1883 class NotifyOffThreadScriptCompletedTask : public Task {
1884 public:
1885 NotifyOffThreadScriptCompletedTask(nsIOffThreadScriptReceiver* aReceiver,
1886 ScriptCompileTask* aCompileTask)
1887 : Task(Kind::MainThreadOnly, EventQueuePriority::Normal),
1888 mReceiver(aReceiver),
1889 mCompileTask(aCompileTask) {}
1891 TaskResult Run() override {
1892 MOZ_ASSERT(NS_IsMainThread());
1894 if (PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) {
1895 return TaskResult::Complete;
1898 RefPtr<JS::Stencil> stencil = mCompileTask->StealStencil();
1899 mCompileTask = nullptr;
1901 (void)mReceiver->OnScriptCompileComplete(
1902 stencil, stencil ? NS_OK : NS_ERROR_FAILURE);
1904 return TaskResult::Complete;
1907 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
1908 bool GetName(nsACString& aName) override {
1909 aName.AssignLiteral("NotifyOffThreadScriptCompletedTask");
1910 return true;
1912 #endif
1914 private:
1915 // NOTE:
1916 // This field is main-thread only, and this task shouldn't be freed off
1917 // main thread.
1919 // This is guaranteed by not having off-thread tasks which depends on this
1920 // task, or any other pointer from off-thread task to this task, because
1921 // otherwise the off-thread task's mDependencies can be the last reference,
1922 // which results in freeing this task off main thread.
1924 // If such task is added, this field must be moved to separate storage.
1925 nsCOMPtr<nsIOffThreadScriptReceiver> mReceiver;
1927 RefPtr<ScriptCompileTask> mCompileTask;
1930 nsresult StartOffThreadCompile(JS::CompileOptions& aOptions,
1931 UniquePtr<Utf8Unit[], JS::FreePolicy>&& aText,
1932 size_t aTextLength,
1933 nsIOffThreadScriptReceiver* aOffThreadReceiver) {
1934 RefPtr<ScriptCompileTask> compileTask =
1935 new ScriptCompileTask(std::move(aText), aTextLength);
1937 RefPtr<NotifyOffThreadScriptCompletedTask> notifyTask =
1938 new NotifyOffThreadScriptCompletedTask(aOffThreadReceiver, compileTask);
1940 nsresult rv = compileTask->Init(aOptions);
1941 NS_ENSURE_SUCCESS(rv, rv);
1943 notifyTask->AddDependency(compileTask.get());
1945 TaskController::Get()->AddTask(compileTask.forget());
1946 TaskController::Get()->AddTask(notifyTask.forget());
1948 return NS_OK;
1951 nsresult nsXULPrototypeScript::Compile(const char16_t* aText,
1952 size_t aTextLength, nsIURI* aURI,
1953 uint32_t aLineNo, Document* aDocument) {
1954 AutoJSAPI jsapi;
1955 if (!jsapi.Init(xpc::CompilationScope())) {
1956 return NS_ERROR_UNEXPECTED;
1958 JSContext* cx = jsapi.cx();
1960 JS::SourceText<char16_t> srcBuf;
1961 if (NS_WARN_IF(!srcBuf.init(cx, aText, aTextLength,
1962 JS::SourceOwnership::Borrowed))) {
1963 return NS_ERROR_FAILURE;
1966 nsAutoCString urlspec;
1967 nsresult rv = aURI->GetSpec(urlspec);
1968 if (NS_WARN_IF(NS_FAILED(rv))) {
1969 return rv;
1972 JS::CompileOptions options(cx);
1973 FillCompileOptions(options, urlspec.get(), aLineNo);
1975 RefPtr<JS::Stencil> stencil =
1976 JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
1977 if (!stencil) {
1978 return NS_ERROR_OUT_OF_MEMORY;
1980 Set(stencil);
1981 return NS_OK;
1984 nsresult nsXULPrototypeScript::CompileMaybeOffThread(
1985 mozilla::UniquePtr<mozilla::Utf8Unit[], JS::FreePolicy>&& aText,
1986 size_t aTextLength, nsIURI* aURI, uint32_t aLineNo, Document* aDocument,
1987 nsIOffThreadScriptReceiver* aOffThreadReceiver) {
1988 MOZ_ASSERT(aOffThreadReceiver);
1990 nsAutoCString urlspec;
1991 nsresult rv = aURI->GetSpec(urlspec);
1992 if (NS_WARN_IF(NS_FAILED(rv))) {
1993 return rv;
1996 AutoJSAPI jsapi;
1997 if (!jsapi.Init(xpc::CompilationScope())) {
1998 return NS_ERROR_UNEXPECTED;
2000 JSContext* cx = jsapi.cx();
2002 JS::CompileOptions options(cx);
2003 FillCompileOptions(options, urlspec.get(), aLineNo);
2005 // TODO: This uses the same heuristics and the same threshold as the
2006 // JS::CanDecodeOffThread API, but the heuristics needs to be updated
2007 // to reflect the change regarding the Stencil API, and also the thread
2008 // management on the consumer side (bug 1840831).
2009 static constexpr size_t OffThreadMinimumTextLength = 5 * 1000;
2011 if (StaticPrefs::javascript_options_parallel_parsing() &&
2012 aTextLength >= OffThreadMinimumTextLength) {
2013 rv = StartOffThreadCompile(options, std::move(aText), aTextLength,
2014 aOffThreadReceiver);
2015 if (NS_WARN_IF(NS_FAILED(rv))) {
2016 return rv;
2018 } else {
2019 JS::SourceText<Utf8Unit> srcBuf;
2020 if (NS_WARN_IF(!srcBuf.init(cx, aText.get(), aTextLength,
2021 JS::SourceOwnership::Borrowed))) {
2022 return NS_ERROR_FAILURE;
2025 RefPtr<JS::Stencil> stencil =
2026 JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
2027 if (!stencil) {
2028 return NS_ERROR_OUT_OF_MEMORY;
2030 Set(stencil);
2032 return NS_OK;
2035 nsresult nsXULPrototypeScript::InstantiateScript(
2036 JSContext* aCx, JS::MutableHandle<JSScript*> aScript) {
2037 MOZ_ASSERT(mStencil);
2039 JS::CompileOptions options(aCx);
2040 JS::InstantiateOptions instantiateOptions(options);
2041 aScript.set(JS::InstantiateGlobalStencil(aCx, instantiateOptions, mStencil));
2042 if (!aScript) {
2043 JS_ClearPendingException(aCx);
2044 return NS_ERROR_OUT_OF_MEMORY;
2047 return NS_OK;
2050 void nsXULPrototypeScript::Set(JS::Stencil* aStencil) { mStencil = aStencil; }
2052 //----------------------------------------------------------------------
2054 // nsXULPrototypeText
2057 nsresult nsXULPrototypeText::Serialize(
2058 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
2059 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
2060 nsresult rv;
2062 // Write basic prototype data
2063 rv = aStream->Write32(mType);
2065 nsresult tmp = aStream->WriteWStringZ(mValue.get());
2066 if (NS_FAILED(tmp)) {
2067 rv = tmp;
2070 return rv;
2073 nsresult nsXULPrototypeText::Deserialize(
2074 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
2075 nsIURI* aDocumentURI,
2076 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
2077 nsresult rv = aStream->ReadString(mValue);
2078 if (NS_WARN_IF(NS_FAILED(rv))) {
2079 return rv;
2081 return NS_OK;
2084 //----------------------------------------------------------------------
2086 // nsXULPrototypePI
2089 nsresult nsXULPrototypePI::Serialize(
2090 nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
2091 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
2092 nsresult rv;
2094 // Write basic prototype data
2095 rv = aStream->Write32(mType);
2097 nsresult tmp = aStream->WriteWStringZ(mTarget.get());
2098 if (NS_FAILED(tmp)) {
2099 rv = tmp;
2101 tmp = aStream->WriteWStringZ(mData.get());
2102 if (NS_FAILED(tmp)) {
2103 rv = tmp;
2106 return rv;
2109 nsresult nsXULPrototypePI::Deserialize(
2110 nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
2111 nsIURI* aDocumentURI,
2112 const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
2113 nsresult rv;
2115 rv = aStream->ReadString(mTarget);
2116 if (NS_FAILED(rv)) return rv;
2117 rv = aStream->ReadString(mData);
2118 if (NS_FAILED(rv)) return rv;
2120 return rv;