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"
10 #include "AttrArray.h"
11 #include "MainThreadUtils.h"
12 #include "ReferrerInfo.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"
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"
77 #include "nsAttrValueInlines.h"
78 #include "nsCaseTreatment.h"
79 #include "nsChangeHint.h"
81 #include "nsCompatibility.h"
82 #include "nsContentCreatorFunctions.h"
83 #include "nsContentUtils.h"
84 #include "nsCycleCollectionNoteChild.h"
85 #include "nsCycleCollectionTraversalCallback.h"
88 #include "nsFocusManager.h"
89 #include "nsGkAtoms.h"
90 #include "nsIContent.h"
91 #include "nsIContentSecurityPolicy.h"
92 #include "nsIControllers.h"
94 #include "nsIDOMEventListener.h"
95 #include "nsIDOMXULControlElement.h"
96 #include "nsIDOMXULSelectCntrlItemEl.h"
97 #include "nsIDocShell.h"
98 #include "nsIFocusManager.h"
100 #include "nsIObjectInputStream.h"
101 #include "nsIObjectOutputStream.h"
102 #include "nsIRunnable.h"
103 #include "nsIScriptContext.h"
104 #include "nsISupportsUtils.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
;
135 #define NS_DISPATCH_XUL_COMMAND (1 << 0)
137 //----------------------------------------------------------------------
141 nsXULElement::nsXULElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
142 : nsStyledElement(std::move(aNodeInfo
)) {
143 XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements
);
146 nsXULElement::~nsXULElement() = default;
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());
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());
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
);
231 nsXULElement
* element
= FromNode(baseElement
);
233 if (aPrototype
->mHasIdAttribute
) {
236 if (aPrototype
->mHasClassAttribute
) {
237 element
->SetMayHaveClass();
239 if (aPrototype
->mHasStyleAttribute
) {
240 element
->SetMayHaveStyle();
243 element
->MakeHeavyweight(aPrototype
);
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
>();
259 nsresult
nsXULElement::CreateFromPrototype(nsXULPrototypeElement
* aPrototype
,
261 bool aIsScriptable
, bool aIsRoot
,
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
;
272 mozilla::dom::NodeInfo
* ni
= aPrototype
->mNodeInfo
;
273 nodeInfo
= aDocument
->NodeInfoManager()->GetNodeInfo(
274 ni
->NameAtom(), ni
->GetPrefixAtom(), ni
->NamespaceID(), ELEMENT_NODE
);
276 nodeInfo
= aPrototype
->mNodeInfo
;
279 RefPtr
<nsXULElement
> element
=
280 CreateFromPrototype(aPrototype
, nodeInfo
, aIsScriptable
, aIsRoot
);
281 element
.forget(aResult
);
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");
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 //----------------------------------------------------------------------
331 nsresult
nsXULElement::Clone(mozilla::dom::NodeInfo
* aNodeInfo
,
332 nsINode
** aResult
) const {
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
);
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
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
);
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(
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
381 if (aWithMouse
&& IsNonList(mNodeInfo
) &&
382 !EventStateManager::IsTopLevelRemoteTarget(this)) {
383 return XULFocusability::NeverFocusable();
387 XULFocusability result
;
388 nsCOMPtr
<nsIDOMXULControlElement
> xulControl
= AsXULControl();
390 // A disabled element cannot be focused and is not part of the tab order
392 xulControl
->GetDisabled(&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
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);
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
;
425 focusable
? focusability
.mForcedTabIndexIfFocusable
.valueOr(-1) : -1};
428 bool nsXULElement::HasMenu() {
429 if (auto* button
= XULButtonElement::FromNode(this)) {
430 return button
->IsMenu();
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();
447 // Nothing will happen if this element isn't a menu.
448 pm
->ShowMenu(this, false);
450 // Nothing will happen if this element isn't a menu.
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();
468 return Err(NS_ERROR_UNEXPECTED
);
471 RefPtr
<Element
> element
= document
->GetElementById(control
);
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()) {
503 controlItem
->GetDisabled(&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
,
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
)) {
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
{
559 explicit XULInContentErrorReporter(Document
& aDocument
)
560 : mozilla::Runnable("XULInContentErrorReporter"), mDocument(aDocument
) {}
562 NS_IMETHOD
Run() override
{
563 mDocument
->WarnOnceAbout(DeprecatedOperations::eImportXULIntoContent
,
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.
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()) {
591 Document
& doc
= aContext
.OwnerDoc();
592 if (!IsInNativeAnonymousSubtree() && !doc
.AllowXULXBL() &&
593 !doc
.HasWarnedAbout(DeprecatedOperations::eImportXULIntoContent
)) {
594 nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(doc
));
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
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");
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
);
624 nsCOMPtr
<nsIContentSecurityPolicy
> docCSP
= doc
.GetCsp();
625 uint32_t policyCount
= 0;
627 docCSP
->GetPolicyCount(&policyCount
);
629 MOZ_ASSERT(policyCount
== 0, "how come we already have a policy?");
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)) {
647 if (XULBroadcastManager::MayNeedListener(*this)) {
648 if (!doc
.HasXULBroadcastManager()) {
649 doc
.InitializeXULBroadcastManager();
651 XULBroadcastManager
* broadcastManager
= doc
.GetXULBroadcastManager();
652 broadcastManager
->AddListener(this);
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
687 nsExtendedDOMSlots
* slots
= GetExistingExtendedDOMSlots();
689 slots
->mControllers
= nullptr;
692 nsStyledElement::UnbindFromTree(aContext
);
695 void nsXULElement::DoneAddingChildren(bool aHaveNotified
) {
696 if (IsXULElement(nsGkAtoms::linkset
)) {
697 Document
* doc
= GetComposedDoc();
699 doc
->OnL10nResourceContainerParsed();
704 void nsXULElement::RegUnRegAccessKey(bool aDoReg
) {
705 // Don't try to register for unsupported elements
706 if (!SupportsAccessKey()) {
710 nsStyledElement::RegUnRegAccessKey(aDoReg
);
713 bool nsXULElement::SupportsAccessKey() const {
714 if (NodeInfo()->Equals(nsGkAtoms::label
) && HasAttr(nsGkAtoms::control
)) {
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
)) {
725 return IsAnyOfXULElements(nsGkAtoms::button
, nsGkAtoms::toolbarbutton
,
726 nsGkAtoms::checkbox
, nsGkAtoms::tab
,
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);
753 } else if (aName
== nsGkAtoms::usercontextid
) {
754 const nsAttrValue
* oldValue
= GetParsedAttr(aName
);
755 if (oldValue
&& (!aValue
|| !aValue
->Equals(*oldValue
))) {
757 "Changing usercontextid doesn't really work properly.");
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
) {
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
)) {
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();
816 listener
->AddTooltipSupport(this);
819 void nsXULElement::RemoveTooltipSupport() {
820 nsXULTooltipListener
* listener
= nsXULTooltipListener::GetInstance();
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
);
846 void nsXULElement::DestroyContent() {
847 nsExtendedDOMSlots
* slots
= GetExistingExtendedDOMSlots();
849 slots
->mControllers
= nullptr;
852 nsStyledElement::DestroyContent();
856 void nsXULElement::List(FILE* out
, int32_t aIndent
) const {
857 nsCString
prefix("XUL");
863 nsStyledElement::List(out
, aIndent
, prefix
);
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
);
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
886 RefPtr
<Event
> event
= aVisitor
.mDOMEvent
;
887 uint16_t inputSource
= MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
890 NS_ENSURE_STATE(event
->GetOriginalTarget() != commandElt
);
891 RefPtr
<XULCommandEvent
> commandEvent
= event
->AsXULCommandEvent();
893 event
= commandEvent
->GetSourceEvent();
894 inputSource
= commandEvent
->InputSource();
895 button
= commandEvent
->Button();
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
);
906 NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
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);
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
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
;
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
958 nsXULElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
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
983 RefPtr
<nsPresContext
> context
= doc
->GetPresContext();
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
=
1000 nsEventStatus status
= nsEventStatus_eIgnore
;
1001 EventDispatcher::Dispatch(this, context
, &eventDown
, nullptr, &status
);
1004 status
= nsEventStatus_eIgnore
; // reset status
1005 EventDispatcher::Dispatch(this, context
, &eventUp
, nullptr, &status
);
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
) {
1019 // oncommand is fired when an element is clicked...
1023 void nsXULElement::DoCommand() {
1024 nsCOMPtr
<Document
> doc
= GetComposedDoc(); // strong just in case
1026 RefPtr
<nsXULElement
> self
= this;
1027 nsContentUtils::DispatchXULCommand(self
, true);
1031 nsresult
nsXULElement::AddPopupListener(nsAtom
* aName
) {
1032 // Add a popup listener to the element
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
)) {
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
);
1050 manager
->AddEventListenerByType(listener
, u
"contextmenu"_ns
,
1051 TrustedEventsAtSystemGroupBubble());
1053 manager
->AddEventListenerByType(listener
, u
"mousedown"_ns
,
1054 TrustedEventsAtSystemGroupBubble());
1059 //----------------------------------------------------------------------
1061 nsresult
nsXULElement::MakeHeavyweight(nsXULPrototypeElement
* aPrototype
) {
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
);
1082 attrValue
.SetTo(protoattr
->mValue
);
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
,
1091 rv
= mAttrs
.SetAndSwapAttr(protoattr
->mName
.NodeInfo(), attrValue
,
1094 NS_ENSURE_SUCCESS(rv
, rv
);
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
));
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
) {
1168 // Write basic prototype data
1169 rv
= aStream
->Write32(mType
);
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
)) {
1180 tmp
= aStream
->Write32(mAttributes
.Length());
1181 if (NS_FAILED(tmp
)) {
1185 nsAutoString attributeValue
;
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");
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
)) {
1205 mAttributes
[i
].mValue
.ToString(attributeValue
);
1206 tmp
= aStream
->WriteWStringZ(attributeValue
.get());
1207 if (NS_FAILED(tmp
)) {
1212 // Now write children
1213 tmp
= aStream
->Write32(uint32_t(mChildren
.Length()));
1214 if (NS_FAILED(tmp
)) {
1217 for (i
= 0; i
< mChildren
.Length(); i
++) {
1218 nsXULPrototypeNode
* child
= mChildren
[i
].get();
1219 switch (child
->mType
) {
1223 tmp
= child
->Serialize(aStream
, aProtoDoc
, aNodeInfos
);
1224 if (NS_FAILED(tmp
)) {
1229 tmp
= aStream
->Write32(child
->mType
);
1230 if (NS_FAILED(tmp
)) {
1233 nsXULPrototypeScript
* script
=
1234 static_cast<nsXULPrototypeScript
*>(child
);
1236 tmp
= aStream
->Write8(script
->mOutOfLine
);
1237 if (NS_FAILED(tmp
)) {
1240 if (!script
->mOutOfLine
) {
1241 tmp
= script
->Serialize(aStream
, aProtoDoc
, aNodeInfos
);
1242 if (NS_FAILED(tmp
)) {
1246 tmp
= aStream
->WriteCompoundObject(script
->mSrcURI
,
1247 NS_GET_IID(nsIURI
), true);
1248 if (NS_FAILED(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
1258 tmp
= script
->SerializeOutOfLine(aStream
, aProtoDoc
);
1259 if (NS_FAILED(tmp
)) {
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");
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);
1283 return NS_ERROR_UNEXPECTED
;
1286 if (mNodeInfo
->Equals(nsGkAtoms::parsererror
) &&
1287 mNodeInfo
->NamespaceEquals(
1288 nsDependentAtomString(nsGkAtoms::nsuri_parsererror
))) {
1289 return NS_ERROR_UNEXPECTED
;
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);
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
) {
1336 child
= new nsXULPrototypeElement();
1337 rv
= child
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
, aNodeInfos
);
1338 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1341 child
= new nsXULPrototypeText();
1342 rv
= child
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
, aNodeInfos
);
1343 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1346 child
= new nsXULPrototypePI();
1347 rv
= child
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
, aNodeInfos
);
1348 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
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
,
1359 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
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
);
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
;
1397 nsresult
nsXULPrototypeElement::SetAttrAt(uint32_t aPos
,
1398 const nsAString
& aValue
,
1399 nsIURI
* aDocumentURI
) {
1400 MOZ_ASSERT(aPos
< mAttributes
.Length(), "out-of-bounds");
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();
1416 mAttributes
[aPos
].mValue
.ParseStringOrAtom(aValue
);
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
);
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();
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
);
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
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
);
1459 mAttributes
[aPos
].mValue
.SetTo(declaration
.forget(), &aValue
);
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
);
1470 mAttributes
[aPos
].mValue
.ParseStringOrAtom(aValue
);
1475 void nsXULPrototypeElement::Unlink() {
1476 mAttributes
.Clear();
1480 //----------------------------------------------------------------------
1482 // nsXULPrototypeScript
1485 nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo
)
1486 : nsXULPrototypeNode(eType_Script
),
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
));
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
);
1536 nsresult rv
= aStream
->Read32(&size
);
1537 if (NS_FAILED(rv
)) {
1542 rv
= aStream
->ReadBytes(size
, &data
);
1543 if (NS_FAILED(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
);
1573 void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions
& aOptions
,
1574 const char* aFilename
,
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
);
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
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?");
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
)) {
1643 tmp
= cache
->FinishScriptOutputStream(mSrcURI
);
1644 if (NS_FAILED(tmp
)) {
1648 if (NS_FAILED(rv
)) cache
->AbortCaching();
1652 nsresult
nsXULPrototypeScript::Deserialize(
1653 nsIObjectInputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
1654 nsIURI
* aDocumentURI
,
1655 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
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
;
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
);
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
;
1687 bool useXULCache
= true;
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();
1701 RefPtr
<JS::Stencil
> newStencil
= cache
->GetStencil(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
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
);
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();
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
);
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();
1759 "Had compilation error in ScriptCompileTask: %s at %s:%u:%u",
1760 message
, filename
, report
->lineno
,
1761 report
->column
.oneOriginValue())
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();
1794 "Had compilation warning in ScriptCompileTask: %s at %s:%u:%u",
1795 message
, filename
, report
->lineno
, report
->column
.oneOriginValue())
1801 class ScriptCompileTask final
: public Task
{
1803 explicit ScriptCompileTask(UniquePtr
<Utf8Unit
[], JS::FreePolicy
>&& aText
,
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
;
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
))) {
1842 JS::CompilationStorage compileStorage
;
1843 mStencil
= JS::CompileGlobalScriptToStencil(mFrontendContext
, mOptions
,
1844 srcBuf
, compileStorage
);
1846 // Chrome-privileged code shouldn't have any compilation error.
1847 CheckErrorsAndWarnings(mFrontendContext
, mOptions
);
1848 MOZ_ASSERT(mStencil
);
1853 TaskResult
Run() override
{
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");
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
;
1883 class NotifyOffThreadScriptCompletedTask
: public Task
{
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");
1916 // This field is main-thread only, and this task shouldn't be freed off
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
,
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());
1951 nsresult
nsXULPrototypeScript::Compile(const char16_t
* aText
,
1952 size_t aTextLength
, nsIURI
* aURI
,
1953 uint32_t aLineNo
, Document
* aDocument
) {
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
))) {
1972 JS::CompileOptions
options(cx
);
1973 FillCompileOptions(options
, urlspec
.get(), aLineNo
);
1975 RefPtr
<JS::Stencil
> stencil
=
1976 JS::CompileGlobalScriptToStencil(cx
, options
, srcBuf
);
1978 return NS_ERROR_OUT_OF_MEMORY
;
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
))) {
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
))) {
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
);
2028 return NS_ERROR_OUT_OF_MEMORY
;
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
));
2043 JS_ClearPendingException(aCx
);
2044 return NS_ERROR_OUT_OF_MEMORY
;
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
) {
2062 // Write basic prototype data
2063 rv
= aStream
->Write32(mType
);
2065 nsresult tmp
= aStream
->WriteWStringZ(mValue
.get());
2066 if (NS_FAILED(tmp
)) {
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
))) {
2084 //----------------------------------------------------------------------
2089 nsresult
nsXULPrototypePI::Serialize(
2090 nsIObjectOutputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
2091 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
2094 // Write basic prototype data
2095 rv
= aStream
->Write32(mType
);
2097 nsresult tmp
= aStream
->WriteWStringZ(mTarget
.get());
2098 if (NS_FAILED(tmp
)) {
2101 tmp
= aStream
->WriteWStringZ(mData
.get());
2102 if (NS_FAILED(tmp
)) {
2109 nsresult
nsXULPrototypePI::Deserialize(
2110 nsIObjectInputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
2111 nsIURI
* aDocumentURI
,
2112 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
2115 rv
= aStream
->ReadString(mTarget
);
2116 if (NS_FAILED(rv
)) return rv
;
2117 rv
= aStream
->ReadString(mData
);
2118 if (NS_FAILED(rv
)) return rv
;