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 "XULFrameElement.h"
15 #include "XULMenuElement.h"
16 #include "XULPopupElement.h"
17 #include "XULTextElement.h"
18 #include "XULTooltipElement.h"
19 #include "XULTreeElement.h"
20 #include "js/CompilationAndEvaluation.h"
21 #include "js/CompileOptions.h"
22 #include "js/experimental/JSStencil.h"
23 #include "js/OffThreadScriptCompilation.h"
24 #include "js/SourceText.h"
25 #include "js/Transcoding.h"
26 #include "js/Utility.h"
28 #include "mozilla/Assertions.h"
29 #include "mozilla/ArrayIterator.h"
30 #include "mozilla/ClearOnShutdown.h"
31 #include "mozilla/DeclarationBlock.h"
32 #include "mozilla/EventDispatcher.h"
33 #include "mozilla/EventListenerManager.h"
34 #include "mozilla/EventStateManager.h"
35 #include "mozilla/FlushType.h"
36 #include "mozilla/GlobalKeyListener.h"
37 #include "mozilla/HoldDropJSObjects.h"
38 #include "mozilla/MacroForEach.h"
39 #include "mozilla/Maybe.h"
40 #include "mozilla/MouseEvents.h"
41 #include "mozilla/OwningNonNull.h"
42 #include "mozilla/PresShell.h"
43 #include "mozilla/RefPtr.h"
44 #include "mozilla/ScopeExit.h"
45 #include "mozilla/StaticAnalysisFunctions.h"
46 #include "mozilla/StaticPtr.h"
47 #include "mozilla/URLExtraData.h"
48 #include "mozilla/dom/BindContext.h"
49 #include "mozilla/dom/BorrowedAttrInfo.h"
50 #include "mozilla/dom/CSSRuleBinding.h"
51 #include "mozilla/dom/Document.h"
52 #include "mozilla/dom/DocumentInlines.h"
53 #include "mozilla/dom/Element.h"
54 #include "mozilla/dom/Event.h"
55 #include "mozilla/dom/EventTarget.h"
56 #include "mozilla/dom/FragmentOrElement.h"
57 #include "mozilla/dom/FromParser.h"
58 #include "mozilla/dom/MouseEventBinding.h"
59 #include "mozilla/dom/MutationEventBinding.h"
60 #include "mozilla/dom/NodeInfo.h"
61 #include "mozilla/dom/ReferrerPolicyBinding.h"
62 #include "mozilla/dom/ScriptSettings.h"
63 #include "mozilla/dom/XULBroadcastManager.h"
64 #include "mozilla/dom/XULCommandEvent.h"
65 #include "mozilla/dom/XULElementBinding.h"
66 #include "mozilla/dom/nsCSPUtils.h"
67 #include "mozilla/fallible.h"
69 #include "nsAttrValueInlines.h"
70 #include "nsAttrValueOrString.h"
71 #include "nsCaseTreatment.h"
72 #include "nsChangeHint.h"
74 #include "nsCompatibility.h"
75 #include "nsContentCreatorFunctions.h"
76 #include "nsContentUtils.h"
77 #include "nsCycleCollectionNoteChild.h"
78 #include "nsCycleCollectionTraversalCallback.h"
81 #include "nsFocusManager.h"
82 #include "nsGkAtoms.h"
83 #include "nsIContent.h"
84 #include "nsIContentSecurityPolicy.h"
85 #include "nsIControllers.h"
87 #include "nsIDOMEventListener.h"
88 #include "nsIDOMXULControlElement.h"
89 #include "nsIDOMXULSelectCntrlItemEl.h"
90 #include "nsIDocShell.h"
91 #include "nsIFocusManager.h"
93 #include "nsIObjectInputStream.h"
94 #include "nsIObjectOutputStream.h"
95 #include "nsIRunnable.h"
96 #include "nsIScriptContext.h"
97 #include "nsISupportsUtils.h"
99 #include "nsIXPConnect.h"
100 #include "nsMenuFrame.h"
101 #include "nsMenuPopupFrame.h"
102 #include "nsNodeInfoManager.h"
103 #include "nsPIDOMWindow.h"
104 #include "nsPIDOMWindowInlines.h"
105 #include "nsPresContext.h"
106 #include "nsQueryFrame.h"
107 #include "nsString.h"
108 #include "nsStyledElement.h"
109 #include "nsThreadUtils.h"
110 #include "nsXULControllers.h"
111 #include "nsXULPopupListener.h"
112 #include "nsXULPopupManager.h"
113 #include "nsXULPrototypeCache.h"
114 #include "nsXULTooltipListener.h"
115 #include "xpcpublic.h"
117 using namespace mozilla
;
118 using namespace mozilla::dom
;
120 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
121 uint32_t nsXULPrototypeAttribute::gNumElements
;
122 uint32_t nsXULPrototypeAttribute::gNumAttributes
;
123 uint32_t nsXULPrototypeAttribute::gNumCacheTests
;
124 uint32_t nsXULPrototypeAttribute::gNumCacheHits
;
125 uint32_t nsXULPrototypeAttribute::gNumCacheSets
;
126 uint32_t nsXULPrototypeAttribute::gNumCacheFills
;
129 #define NS_DISPATCH_XUL_COMMAND (1 << 0)
131 //----------------------------------------------------------------------
135 nsXULElement::nsXULElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
136 : nsStyledElement(std::move(aNodeInfo
)) {
137 XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements
);
140 nsXULElement::~nsXULElement() = default;
143 nsXULElement
* NS_NewBasicXULElement(
144 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
) {
145 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo(std::move(aNodeInfo
));
146 auto* nim
= nodeInfo
->NodeInfoManager();
147 return new (nim
) nsXULElement(nodeInfo
.forget());
151 nsXULElement
* nsXULElement::Construct(
152 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
) {
153 // NOTE: If you add elements here, you probably also want to change
154 // mozilla::dom::binding_detail::HTMLConstructor in BindingUtils.cpp to take
155 // them into account, otherwise you'll start getting "Illegal constructor"
156 // exceptions in chrome code.
157 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
= aNodeInfo
;
158 if (nodeInfo
->Equals(nsGkAtoms::label
) ||
159 nodeInfo
->Equals(nsGkAtoms::description
)) {
160 auto* nim
= nodeInfo
->NodeInfoManager();
161 return new (nim
) XULTextElement(nodeInfo
.forget());
164 if (nodeInfo
->Equals(nsGkAtoms::menupopup
) ||
165 nodeInfo
->Equals(nsGkAtoms::popup
) ||
166 nodeInfo
->Equals(nsGkAtoms::panel
)) {
167 return NS_NewXULPopupElement(nodeInfo
.forget());
170 if (nodeInfo
->Equals(nsGkAtoms::tooltip
)) {
171 return NS_NewXULTooltipElement(nodeInfo
.forget());
174 if (nodeInfo
->Equals(nsGkAtoms::iframe
) ||
175 nodeInfo
->Equals(nsGkAtoms::browser
) ||
176 nodeInfo
->Equals(nsGkAtoms::editor
)) {
177 auto* nim
= nodeInfo
->NodeInfoManager();
178 return new (nim
) XULFrameElement(nodeInfo
.forget());
181 if (nodeInfo
->Equals(nsGkAtoms::menu
) ||
182 nodeInfo
->Equals(nsGkAtoms::menulist
)) {
183 auto* nim
= nodeInfo
->NodeInfoManager();
184 return new (nim
) XULMenuElement(nodeInfo
.forget());
187 if (nodeInfo
->Equals(nsGkAtoms::tree
)) {
188 auto* nim
= nodeInfo
->NodeInfoManager();
189 return new (nim
) XULTreeElement(nodeInfo
.forget());
192 return NS_NewBasicXULElement(nodeInfo
.forget());
196 already_AddRefed
<nsXULElement
> nsXULElement::CreateFromPrototype(
197 nsXULPrototypeElement
* aPrototype
, mozilla::dom::NodeInfo
* aNodeInfo
,
198 bool aIsScriptable
, bool aIsRoot
) {
199 RefPtr
<mozilla::dom::NodeInfo
> ni
= aNodeInfo
;
200 nsCOMPtr
<Element
> baseElement
;
201 NS_NewXULElement(getter_AddRefs(baseElement
), ni
.forget(),
202 dom::FROM_PARSER_NETWORK
, aPrototype
->mIsAtom
);
205 nsXULElement
* element
= FromNode(baseElement
);
207 if (aPrototype
->mHasIdAttribute
) {
210 if (aPrototype
->mHasClassAttribute
) {
211 element
->SetMayHaveClass();
213 if (aPrototype
->mHasStyleAttribute
) {
214 element
->SetMayHaveStyle();
217 element
->MakeHeavyweight(aPrototype
);
219 // Check each attribute on the prototype to see if we need to do
220 // any additional processing and hookup that would otherwise be
221 // done 'automagically' by SetAttr().
222 for (const auto& attribute
: aPrototype
->mAttributes
) {
223 element
->AddListenerForAttributeIfNeeded(attribute
.mName
);
227 return baseElement
.forget().downcast
<nsXULElement
>();
233 nsresult
nsXULElement::CreateFromPrototype(nsXULPrototypeElement
* aPrototype
,
235 bool aIsScriptable
, bool aIsRoot
,
237 // Create an nsXULElement from a prototype
238 MOZ_ASSERT(aPrototype
!= nullptr, "null ptr");
239 if (!aPrototype
) return NS_ERROR_NULL_POINTER
;
241 MOZ_ASSERT(aResult
!= nullptr, "null ptr");
242 if (!aResult
) return NS_ERROR_NULL_POINTER
;
244 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
246 mozilla::dom::NodeInfo
* ni
= aPrototype
->mNodeInfo
;
247 nodeInfo
= aDocument
->NodeInfoManager()->GetNodeInfo(
248 ni
->NameAtom(), ni
->GetPrefixAtom(), ni
->NamespaceID(), ELEMENT_NODE
);
250 nodeInfo
= aPrototype
->mNodeInfo
;
253 RefPtr
<nsXULElement
> element
=
254 CreateFromPrototype(aPrototype
, nodeInfo
, aIsScriptable
, aIsRoot
);
255 element
.forget(aResult
);
260 nsresult
NS_NewXULElement(Element
** aResult
,
261 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
262 FromParser aFromParser
, nsAtom
* aIsAtom
,
263 mozilla::dom::CustomElementDefinition
* aDefinition
) {
264 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
= aNodeInfo
;
266 MOZ_ASSERT(nodeInfo
, "need nodeinfo for non-proto Create");
269 nodeInfo
->NamespaceEquals(kNameSpaceID_XUL
),
270 "Trying to create XUL elements that don't have the XUL namespace");
272 Document
* doc
= nodeInfo
->GetDocument();
273 if (doc
&& !doc
->AllowXULXBL()) {
274 return NS_ERROR_NOT_AVAILABLE
;
277 return nsContentUtils::NewXULOrHTMLElement(aResult
, nodeInfo
, aFromParser
,
278 aIsAtom
, aDefinition
);
281 void NS_TrustedNewXULElement(
282 Element
** aResult
, already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
) {
283 RefPtr
<mozilla::dom::NodeInfo
> ni
= aNodeInfo
;
284 MOZ_ASSERT(ni
, "need nodeinfo for non-proto Create");
286 // Create an nsXULElement with the specified namespace and tag.
287 NS_ADDREF(*aResult
= nsXULElement::Construct(ni
.forget()));
290 //----------------------------------------------------------------------
291 // nsISupports interface
293 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULElement
, nsStyledElement
)
295 NS_IMPL_ADDREF_INHERITED(nsXULElement
, nsStyledElement
)
296 NS_IMPL_RELEASE_INHERITED(nsXULElement
, nsStyledElement
)
298 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement
)
299 NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
300 NS_INTERFACE_MAP_END_INHERITING(nsStyledElement
)
302 //----------------------------------------------------------------------
305 nsresult
nsXULElement::Clone(mozilla::dom::NodeInfo
* aNodeInfo
,
306 nsINode
** aResult
) const {
309 RefPtr
<mozilla::dom::NodeInfo
> ni
= aNodeInfo
;
310 RefPtr
<nsXULElement
> element
= Construct(ni
.forget());
312 nsresult rv
= const_cast<nsXULElement
*>(this)->CopyInnerTo(
313 element
, ReparseAttributes::No
);
314 NS_ENSURE_SUCCESS(rv
, rv
);
316 // Note that we're _not_ copying mControllers.
318 element
.forget(aResult
);
322 //----------------------------------------------------------------------
324 EventListenerManager
* nsXULElement::GetEventListenerManagerForAttr(
325 nsAtom
* aAttrName
, bool* aDefer
) {
326 // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc()
327 // here, override BindToTree for those classes and munge event
329 Document
* doc
= OwnerDoc();
331 nsPIDOMWindowInner
* window
;
332 Element
* root
= doc
->GetRootElement();
333 if ((!root
|| root
== this) && (window
= doc
->GetInnerWindow())) {
334 nsCOMPtr
<EventTarget
> piTarget
= do_QueryInterface(window
);
337 return piTarget
->GetOrCreateListenerManager();
340 return nsStyledElement::GetEventListenerManagerForAttr(aAttrName
, aDefer
);
343 // returns true if the element is not a list
344 static bool IsNonList(mozilla::dom::NodeInfo
* aNodeInfo
) {
345 return !aNodeInfo
->Equals(nsGkAtoms::tree
) &&
346 !aNodeInfo
->Equals(nsGkAtoms::richlistbox
);
349 bool nsXULElement::IsFocusableInternal(int32_t* aTabIndex
, bool aWithMouse
) {
351 * Returns true if an element may be focused, and false otherwise. The inout
352 * argument aTabIndex will be set to the tab order index to be used; -1 for
353 * elements that should not be part of the tab order and a greater value to
354 * indicate its tab order.
356 * Confusingly, the supplied value for the aTabIndex argument may indicate
357 * whether the element may be focused as a result of the -moz-user-focus
358 * property, where -1 means no and 0 means yes.
360 * For controls, the element cannot be focused and is not part of the tab
361 * order if it is disabled.
363 * -moz-user-focus is overridden if a tabindex (even -1) is specified.
365 * Specifically, the behaviour for all XUL elements is as follows:
366 * *aTabIndex = -1 no tabindex Not focusable or tabbable
367 * *aTabIndex = -1 tabindex="-1" Focusable but not tabbable
368 * *aTabIndex = -1 tabindex=">=0" Focusable and tabbable
369 * *aTabIndex >= 0 no tabindex Focusable and tabbable
370 * *aTabIndex >= 0 tabindex="-1" Focusable but not tabbable
371 * *aTabIndex >= 0 tabindex=">=0" Focusable and tabbable
373 * If aTabIndex is null, then the tabindex is not computed, and
374 * true is returned for non-disabled controls and false otherwise.
377 // elements are not focusable by default
378 bool shouldFocus
= false;
381 // on Mac, mouse interactions only focus the element if it's a list,
382 // or if it's a remote target, since the remote target must handle
384 if (aWithMouse
&& IsNonList(mNodeInfo
) &&
385 !EventStateManager::IsTopLevelRemoteTarget(this)) {
390 nsCOMPtr
<nsIDOMXULControlElement
> xulControl
= AsXULControl();
392 // a disabled element cannot be focused and is not part of the tab order
394 xulControl
->GetDisabled(&disabled
);
396 if (aTabIndex
) *aTabIndex
= -1;
403 Maybe
<int32_t> attrVal
= GetTabIndexAttrValue();
404 if (attrVal
.isSome()) {
405 // The tabindex attribute was specified, so the element becomes
408 *aTabIndex
= attrVal
.value();
410 // otherwise, if there is no tabindex attribute, just use the value of
411 // *aTabIndex to indicate focusability. Reset any supplied tabindex to 0.
412 shouldFocus
= *aTabIndex
>= 0;
418 if (xulControl
&& shouldFocus
&& sTabFocusModelAppliesToXUL
&&
419 !(sTabFocusModel
& eTabFocus_formElementsMask
)) {
420 // By default, the tab focus model doesn't apply to xul element on any
421 // system but OS X. on OS X we're following it for UI elements (XUL) as
422 // sTabFocusModel is based on "Full Keyboard Access" system setting (see
423 // mac/nsILookAndFeel). both textboxes and list elements (i.e. trees and
424 // list) should always be focusable (textboxes are handled as html:input)
425 // For compatibility, we only do this for controls, otherwise elements
426 // like <browser> cannot take this focus.
427 if (IsNonList(mNodeInfo
)) {
436 int32_t nsXULElement::ScreenX() {
437 nsIFrame
* frame
= GetPrimaryFrame(FlushType::Layout
);
438 return frame
? frame
->GetScreenRect().x
: 0;
441 int32_t nsXULElement::ScreenY() {
442 nsIFrame
* frame
= GetPrimaryFrame(FlushType::Layout
);
443 return frame
? frame
->GetScreenRect().y
: 0;
446 bool nsXULElement::HasMenu() {
447 nsMenuFrame
* menu
= do_QueryFrame(GetPrimaryFrame(FlushType::Frames
));
451 void nsXULElement::OpenMenu(bool aOpenFlag
) {
452 // Flush frames first. It's not clear why this is needed, see bug 1704670.
453 if (Document
* doc
= GetComposedDoc()) {
454 doc
->FlushPendingNotifications(FlushType::Frames
);
457 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
460 // Nothing will happen if this element isn't a menu.
461 pm
->ShowMenu(this, false, false);
463 // Nothing will happen if this element isn't a menu.
469 Result
<bool, nsresult
> nsXULElement::PerformAccesskey(bool aKeyCausesActivation
,
470 bool aIsTrustedEvent
) {
471 if (IsXULElement(nsGkAtoms::label
)) {
472 nsAutoString control
;
473 GetAttr(kNameSpaceID_None
, nsGkAtoms::control
, control
);
474 if (control
.IsEmpty()) {
475 return Err(NS_ERROR_UNEXPECTED
);
478 // XXXsmaug Should we use ShadowRoot::GetElementById in case
479 // element is in Shadow DOM?
480 RefPtr
<Document
> document
= GetUncomposedDoc();
482 return Err(NS_ERROR_UNEXPECTED
);
485 RefPtr
<Element
> element
= document
->GetElementById(control
);
487 return Err(NS_ERROR_UNEXPECTED
);
490 // XXXedgar, This is mainly for HTMLElement which doesn't do visible
491 // check in PerformAccesskey. We probably should always do visible
492 // check on HTMLElement even if the PerformAccesskey is not redirected from
493 // label XULelement per spec.
494 nsIFrame
* frame
= element
->GetPrimaryFrame();
495 if (!frame
|| !frame
->IsVisibleConsideringAncestors()) {
496 return Err(NS_ERROR_UNEXPECTED
);
499 return element
->PerformAccesskey(aKeyCausesActivation
, aIsTrustedEvent
);
502 nsIFrame
* frame
= GetPrimaryFrame();
503 if (!frame
|| !frame
->IsVisibleConsideringAncestors()) {
504 return Err(NS_ERROR_UNEXPECTED
);
507 bool focused
= false;
508 // Define behavior for each type of XUL element.
509 if (!IsXULElement(nsGkAtoms::toolbarbutton
)) {
510 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
511 RefPtr
<Element
> elementToFocus
= this;
512 // for radio buttons, focus the radiogroup instead
513 if (IsXULElement(nsGkAtoms::radio
)) {
514 if (nsCOMPtr
<nsIDOMXULSelectControlItemElement
> controlItem
=
515 AsXULSelectControlItem()) {
517 controlItem
->GetDisabled(&disabled
);
519 controlItem
->GetControl(getter_AddRefs(elementToFocus
));
524 if (elementToFocus
) {
525 fm
->SetFocus(elementToFocus
, nsIFocusManager::FLAG_BYKEY
);
527 // Return true if the element became focused.
528 nsPIDOMWindowOuter
* window
= OwnerDoc()->GetWindow();
529 focused
= (window
&& window
->GetFocusedElement() == elementToFocus
);
534 if (aKeyCausesActivation
&& !IsXULElement(nsGkAtoms::menulist
)) {
535 ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD
,
540 // If the accesskey won't cause the activation and the focus isn't changed,
541 // either. Return error so EventStateManager would try to find next element
542 // to handle the accesskey.
543 return focused
? Result
<bool, nsresult
>{focused
} : Err(NS_ERROR_ABORT
);
546 //----------------------------------------------------------------------
548 void nsXULElement::AddListenerForAttributeIfNeeded(nsAtom
* aLocalName
) {
549 // If appropriate, add a popup listener and/or compile the event
550 // handler. Called when we change the element's document, create a
551 // new element, change an attribute's value, etc.
552 // Eventlistenener-attributes are always in the null namespace.
553 if (aLocalName
== nsGkAtoms::menu
|| aLocalName
== nsGkAtoms::contextmenu
||
554 // XXXdwh popup and context are deprecated
555 aLocalName
== nsGkAtoms::popup
|| aLocalName
== nsGkAtoms::context
) {
556 AddPopupListener(aLocalName
);
558 if (nsContentUtils::IsEventAttributeName(aLocalName
, EventNameType_XUL
)) {
560 GetAttr(kNameSpaceID_None
, aLocalName
, value
);
561 SetEventHandler(aLocalName
, value
, true);
565 void nsXULElement::AddListenerForAttributeIfNeeded(const nsAttrName
& aName
) {
566 if (aName
.IsAtom()) {
567 AddListenerForAttributeIfNeeded(aName
.Atom());
571 //----------------------------------------------------------------------
573 // nsIContent interface
575 void nsXULElement::UpdateEditableState(bool aNotify
) {
576 // Don't call through to Element here because the things
577 // it does don't work for cases when we're an editable control.
578 nsIContent
* parent
= GetParent();
580 SetEditableFlag(parent
&& parent
->HasFlag(NODE_IS_EDITABLE
));
581 UpdateState(aNotify
);
584 class XULInContentErrorReporter
: public Runnable
{
586 explicit XULInContentErrorReporter(Document
& aDocument
)
587 : mozilla::Runnable("XULInContentErrorReporter"), mDocument(aDocument
) {}
589 NS_IMETHOD
Run() override
{
590 mDocument
->WarnOnceAbout(DeprecatedOperations::eImportXULIntoContent
,
596 OwningNonNull
<Document
> mDocument
;
599 static bool NeedTooltipSupport(const nsXULElement
& aXULElement
) {
600 if (aXULElement
.NodeInfo()->Equals(nsGkAtoms::treechildren
)) {
601 // treechildren always get tooltip support, since cropped tree cells show
602 // their full text in a tooltip.
606 return aXULElement
.GetBoolAttr(nsGkAtoms::tooltip
) ||
607 aXULElement
.GetBoolAttr(nsGkAtoms::tooltiptext
);
610 nsresult
nsXULElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
611 nsresult rv
= nsStyledElement::BindToTree(aContext
, aParent
);
612 NS_ENSURE_SUCCESS(rv
, rv
);
614 if (!IsInComposedDoc()) {
618 Document
& doc
= aContext
.OwnerDoc();
619 if (!IsInNativeAnonymousSubtree() && !doc
.AllowXULXBL() &&
620 !doc
.HasWarnedAbout(DeprecatedOperations::eImportXULIntoContent
)) {
621 nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(doc
));
625 if (!doc
.AllowXULXBL() && !doc
.IsUnstyledDocument()) {
626 // To save CPU cycles and memory, non-XUL documents only load the user
627 // agent style sheet rules for a minimal set of XUL elements such as
628 // 'scrollbar' that may be created implicitly for their content (those
629 // rules being in minimal-xul.css).
631 // This assertion makes sure no other XUL element is used in a non-XUL
633 nsAtom
* tag
= NodeInfo()->NameAtom();
636 tag
== nsGkAtoms::scrollbar
|| tag
== nsGkAtoms::scrollbarbutton
||
637 tag
== nsGkAtoms::scrollcorner
|| tag
== nsGkAtoms::slider
||
638 tag
== nsGkAtoms::thumb
||
640 tag
== nsGkAtoms::resizer
|| tag
== nsGkAtoms::label
,
641 "Unexpected XUL element in non-XUL doc");
645 // Within Bug 1492063 and its dependencies we started to apply a
646 // CSP to system privileged about pages. Since some about: pages
647 // are implemented in *.xul files we added this workaround to
648 // apply a CSP to them. To do so, we check the introduced custom
649 // attribute 'csp' on the root element.
650 if (doc
.GetRootElement() == this) {
651 nsAutoString cspPolicyStr
;
652 GetAttr(kNameSpaceID_None
, nsGkAtoms::csp
, cspPolicyStr
);
656 nsCOMPtr
<nsIContentSecurityPolicy
> docCSP
= doc
.GetCsp();
657 uint32_t policyCount
= 0;
659 docCSP
->GetPolicyCount(&policyCount
);
661 MOZ_ASSERT(policyCount
== 0, "how come we already have a policy?");
665 CSP_ApplyMetaCSPToDoc(doc
, cspPolicyStr
);
668 if (NodeInfo()->Equals(nsGkAtoms::keyset
, kNameSpaceID_XUL
)) {
669 // Create our XUL key listener and hook it up.
670 XULKeySetGlobalKeyListener::AttachKeyHandler(this);
673 RegUnRegAccessKey(true);
675 if (NeedTooltipSupport(*this)) {
679 if (XULBroadcastManager::MayNeedListener(*this)) {
680 if (!doc
.HasXULBroadcastManager()) {
681 doc
.InitializeXULBroadcastManager();
683 XULBroadcastManager
* broadcastManager
= doc
.GetXULBroadcastManager();
684 broadcastManager
->AddListener(this);
689 void nsXULElement::UnbindFromTree(bool aNullParent
) {
690 if (NodeInfo()->Equals(nsGkAtoms::keyset
, kNameSpaceID_XUL
)) {
691 XULKeySetGlobalKeyListener::DetachKeyHandler(this);
694 RegUnRegAccessKey(false);
696 if (NeedTooltipSupport(*this)) {
697 RemoveTooltipSupport();
700 Document
* doc
= GetComposedDoc();
701 if (doc
&& doc
->HasXULBroadcastManager() &&
702 XULBroadcastManager::MayNeedListener(*this)) {
703 RefPtr
<XULBroadcastManager
> broadcastManager
=
704 doc
->GetXULBroadcastManager();
705 broadcastManager
->RemoveListener(this);
708 // mControllers can own objects that are implemented
709 // in JavaScript (such as some implementations of
710 // nsIControllers. These objects prevent their global
711 // object's script object from being garbage collected,
712 // which means JS continues to hold an owning reference
713 // to the nsGlobalWindow, which owns the document,
714 // which owns this content. That's a cycle, so we break
715 // it here. (It might be better to break this by releasing
716 // mDocument in nsGlobalWindow::SetDocShell, but I'm not
717 // sure whether that would fix all possible cycles through
719 nsExtendedDOMSlots
* slots
= GetExistingExtendedDOMSlots();
721 slots
->mControllers
= nullptr;
724 nsStyledElement::UnbindFromTree(aNullParent
);
727 void nsXULElement::DoneAddingChildren(bool aHaveNotified
) {
728 if (IsXULElement(nsGkAtoms::linkset
)) {
729 Document
* doc
= GetComposedDoc();
731 doc
->OnL10nResourceContainerParsed();
736 void nsXULElement::RegUnRegAccessKey(bool aDoReg
) {
737 // Don't try to register for unsupported elements
738 if (!SupportsAccessKey()) {
742 nsStyledElement::RegUnRegAccessKey(aDoReg
);
745 bool nsXULElement::SupportsAccessKey() const {
746 if (NodeInfo()->Equals(nsGkAtoms::label
) && HasAttr(nsGkAtoms::control
)) {
750 // XXX(ntim): check if description[value] or description[accesskey] are
751 // actually used, remove `value` from {Before/After}SetAttr if not the case
752 if (NodeInfo()->Equals(nsGkAtoms::description
) && HasAttr(nsGkAtoms::value
) &&
753 HasAttr(nsGkAtoms::control
)) {
757 return IsAnyOfXULElements(nsGkAtoms::button
, nsGkAtoms::toolbarbutton
,
758 nsGkAtoms::checkbox
, nsGkAtoms::tab
,
762 nsresult
nsXULElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
763 const nsAttrValueOrString
* aValue
,
765 if (aNamespaceID
== kNameSpaceID_None
&&
766 (aName
== nsGkAtoms::accesskey
|| aName
== nsGkAtoms::control
||
767 aName
== nsGkAtoms::value
)) {
768 RegUnRegAccessKey(false);
769 } else if (aNamespaceID
== kNameSpaceID_None
&&
770 (aName
== nsGkAtoms::command
|| aName
== nsGkAtoms::observes
) &&
771 IsInUncomposedDoc()) {
772 // XXX sXBL/XBL2 issue! Owner or current document?
773 // XXX Why does this not also remove broadcast listeners if the
774 // "element" attribute was changed on an <observer>?
775 nsAutoString oldValue
;
776 GetAttr(kNameSpaceID_None
, nsGkAtoms::observes
, oldValue
);
777 if (oldValue
.IsEmpty()) {
778 GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, oldValue
);
781 Document
* doc
= GetUncomposedDoc();
782 if (!oldValue
.IsEmpty() && doc
->HasXULBroadcastManager()) {
783 RefPtr
<XULBroadcastManager
> broadcastManager
=
784 doc
->GetXULBroadcastManager();
785 broadcastManager
->RemoveListener(this);
787 } else if (aNamespaceID
== kNameSpaceID_None
&& aValue
&&
788 mNodeInfo
->Equals(nsGkAtoms::window
) &&
789 aName
== nsGkAtoms::chromemargin
) {
790 nsAttrValue attrValue
;
791 // Make sure the margin format is valid first
792 if (!attrValue
.ParseIntMarginValue(aValue
->String())) {
793 return NS_ERROR_INVALID_ARG
;
795 } else if (aNamespaceID
== kNameSpaceID_None
&&
796 aName
== nsGkAtoms::usercontextid
) {
797 nsAutoString oldValue
;
799 GetAttr(kNameSpaceID_None
, nsGkAtoms::usercontextid
, oldValue
);
800 if (hasAttribute
&& (!aValue
|| !aValue
->String().Equals(oldValue
))) {
801 MOZ_ASSERT(false, "Changing usercontextid is not allowed.");
802 return NS_ERROR_INVALID_ARG
;
806 return nsStyledElement::BeforeSetAttr(aNamespaceID
, aName
, aValue
, aNotify
);
809 nsresult
nsXULElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
810 const nsAttrValue
* aValue
,
811 const nsAttrValue
* aOldValue
,
812 nsIPrincipal
* aSubjectPrincipal
,
814 if (aNamespaceID
== kNameSpaceID_None
) {
816 AddListenerForAttributeIfNeeded(aName
);
819 if (aName
== nsGkAtoms::accesskey
|| aName
== nsGkAtoms::control
||
820 aName
== nsGkAtoms::value
) {
821 RegUnRegAccessKey(true);
822 } else if (aName
== nsGkAtoms::tooltip
|| aName
== nsGkAtoms::tooltiptext
) {
823 if (!!aValue
!= !!aOldValue
&& IsInComposedDoc() &&
824 !NodeInfo()->Equals(nsGkAtoms::treechildren
)) {
828 RemoveTooltipSupport();
832 Document
* doc
= GetComposedDoc();
833 if (doc
&& doc
->HasXULBroadcastManager()) {
834 RefPtr
<XULBroadcastManager
> broadcastManager
=
835 doc
->GetXULBroadcastManager();
836 broadcastManager
->AttributeChanged(this, aNamespaceID
, aName
);
838 if (doc
&& XULBroadcastManager::MayNeedListener(*this)) {
839 if (!doc
->HasXULBroadcastManager()) {
840 doc
->InitializeXULBroadcastManager();
842 XULBroadcastManager
* broadcastManager
= doc
->GetXULBroadcastManager();
843 broadcastManager
->AddListener(this);
846 // XXX need to check if they're changing an event handler: if
847 // so, then we need to unhook the old one. Or something.
850 return nsStyledElement::AfterSetAttr(aNamespaceID
, aName
, aValue
, aOldValue
,
851 aSubjectPrincipal
, aNotify
);
854 void nsXULElement::AddTooltipSupport() {
855 nsXULTooltipListener
* listener
= nsXULTooltipListener::GetInstance();
860 listener
->AddTooltipSupport(this);
863 void nsXULElement::RemoveTooltipSupport() {
864 nsXULTooltipListener
* listener
= nsXULTooltipListener::GetInstance();
869 listener
->RemoveTooltipSupport(this);
872 bool nsXULElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
873 const nsAString
& aValue
,
874 nsIPrincipal
* aMaybeScriptedPrincipal
,
875 nsAttrValue
& aResult
) {
876 if (aNamespaceID
== kNameSpaceID_None
&& aAttribute
== nsGkAtoms::tabindex
) {
877 return aResult
.ParseIntValue(aValue
);
880 // Parse into a nsAttrValue
881 if (!nsStyledElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
882 aMaybeScriptedPrincipal
, aResult
)) {
883 // Fall back to parsing as atom for short values
884 aResult
.ParseStringOrAtom(aValue
);
890 void nsXULElement::DestroyContent() {
891 nsExtendedDOMSlots
* slots
= GetExistingExtendedDOMSlots();
893 slots
->mControllers
= nullptr;
896 nsStyledElement::DestroyContent();
900 void nsXULElement::List(FILE* out
, int32_t aIndent
) const {
901 nsCString
prefix("XUL");
907 nsStyledElement::List(out
, aIndent
, prefix
);
911 bool nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage
) {
912 return (IsRootOfNativeAnonymousSubtree() &&
913 IsAnyOfXULElements(nsGkAtoms::scrollbar
, nsGkAtoms::scrollcorner
) &&
914 (aMessage
== eMouseClick
|| aMessage
== eMouseDoubleClick
||
915 aMessage
== eXULCommand
|| aMessage
== eContextMenu
||
916 aMessage
== eDragStart
|| aMessage
== eMouseAuxClick
));
919 nsresult
nsXULElement::DispatchXULCommand(const EventChainVisitor
& aVisitor
,
920 nsAutoString
& aCommand
) {
921 // XXX sXBL/XBL2 issue! Owner or current document?
922 nsCOMPtr
<Document
> doc
= GetUncomposedDoc();
923 NS_ENSURE_STATE(doc
);
924 RefPtr
<Element
> commandElt
= doc
->GetElementById(aCommand
);
926 // Create a new command event to dispatch to the element
927 // pointed to by the command attribute. The new event's
928 // sourceEvent will be the original command event that we're
930 RefPtr
<Event
> event
= aVisitor
.mDOMEvent
;
931 uint16_t inputSource
= MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
;
934 NS_ENSURE_STATE(event
->GetOriginalTarget() != commandElt
);
935 RefPtr
<XULCommandEvent
> commandEvent
= event
->AsXULCommandEvent();
937 event
= commandEvent
->GetSourceEvent();
938 inputSource
= commandEvent
->InputSource();
939 button
= commandEvent
->Button();
944 WidgetInputEvent
* orig
= aVisitor
.mEvent
->AsInputEvent();
945 nsContentUtils::DispatchXULCommand(
946 commandElt
, orig
->IsTrusted(), MOZ_KnownLive(aVisitor
.mDOMEvent
),
947 nullptr, orig
->IsControl(), orig
->IsAlt(), orig
->IsShift(),
948 orig
->IsMeta(), inputSource
, button
);
950 NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
955 void nsXULElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
956 aVisitor
.mForceContentDispatch
= true; // FIXME! Bug 329119
957 if (IsEventStoppedFromAnonymousScrollbar(aVisitor
.mEvent
->mMessage
)) {
958 // Don't propagate these events from native anonymous scrollbar.
959 aVisitor
.mCanHandle
= true;
960 aVisitor
.SetParentTarget(nullptr, false);
963 if (aVisitor
.mEvent
->mMessage
== eXULCommand
&&
964 aVisitor
.mEvent
->mClass
== eInputEventClass
&&
965 aVisitor
.mEvent
->mOriginalTarget
== static_cast<nsIContent
*>(this) &&
966 !IsXULElement(nsGkAtoms::command
)) {
967 // Check that we really have an xul command event. That will be handled
969 // See if we have a command elt. If so, we execute on the command
970 // instead of on our content element.
971 if (aVisitor
.mDOMEvent
&& aVisitor
.mDOMEvent
->AsXULCommandEvent() &&
972 HasNonEmptyAttr(nsGkAtoms::command
)) {
973 // Stop building the event target chain for the original event.
974 // We don't want it to propagate to any DOM nodes.
975 aVisitor
.mCanHandle
= false;
976 aVisitor
.mAutomaticChromeDispatch
= false;
977 // Dispatch XUL command in PreHandleEvent to prevent it breaks event
978 // target chain creation
979 aVisitor
.mWantsPreHandleEvent
= true;
980 aVisitor
.mItemFlags
|= NS_DISPATCH_XUL_COMMAND
;
985 nsStyledElement::GetEventTargetParent(aVisitor
);
988 nsresult
nsXULElement::PreHandleEvent(EventChainVisitor
& aVisitor
) {
989 if (aVisitor
.mItemFlags
& NS_DISPATCH_XUL_COMMAND
) {
990 nsAutoString command
;
991 GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, command
);
992 MOZ_ASSERT(!command
.IsEmpty());
993 return DispatchXULCommand(aVisitor
, command
);
995 return nsStyledElement::PreHandleEvent(aVisitor
);
998 //----------------------------------------------------------------------
999 // Implementation methods
1001 nsChangeHint
nsXULElement::GetAttributeChangeHint(const nsAtom
* aAttribute
,
1002 int32_t aModType
) const {
1003 if (aAttribute
== nsGkAtoms::value
&&
1004 (aModType
== MutationEvent_Binding::REMOVAL
||
1005 aModType
== MutationEvent_Binding::ADDITION
) &&
1006 IsAnyOfXULElements(nsGkAtoms::label
, nsGkAtoms::description
)) {
1007 // Label and description dynamically morph between a normal
1008 // block and a cropping single-line XUL text frame. If the
1009 // value attribute is being added or removed, then we need to
1010 // return a hint of frame change. (See bugzilla bug 95475 for
1012 return nsChangeHint_ReconstructFrame
;
1015 if (aAttribute
== nsGkAtoms::type
&&
1016 IsAnyOfXULElements(nsGkAtoms::toolbarbutton
, nsGkAtoms::button
)) {
1017 // type=menu switches from a button frame to a menu frame.
1018 return nsChangeHint_ReconstructFrame
;
1021 return nsChangeHint(0);
1024 NS_IMETHODIMP_(bool)
1025 nsXULElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
1029 nsIControllers
* nsXULElement::GetControllers(ErrorResult
& rv
) {
1030 if (!Controllers()) {
1031 nsExtendedDOMSlots
* slots
= ExtendedDOMSlots();
1033 slots
->mControllers
= new nsXULControllers();
1036 return Controllers();
1039 void nsXULElement::Click(CallerType aCallerType
) {
1040 ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_UNKNOWN
,
1041 aCallerType
== CallerType::System
);
1044 void nsXULElement::ClickWithInputSource(uint16_t aInputSource
,
1045 bool aIsTrustedEvent
) {
1046 if (BoolAttrIsTrue(nsGkAtoms::disabled
)) return;
1048 nsCOMPtr
<Document
> doc
= GetComposedDoc(); // Strong just in case
1050 RefPtr
<nsPresContext
> context
= doc
->GetPresContext();
1052 // strong ref to PresContext so events don't destroy it
1054 WidgetMouseEvent
eventDown(aIsTrustedEvent
, eMouseDown
, nullptr,
1055 WidgetMouseEvent::eReal
);
1056 WidgetMouseEvent
eventUp(aIsTrustedEvent
, eMouseUp
, nullptr,
1057 WidgetMouseEvent::eReal
);
1058 WidgetMouseEvent
eventClick(aIsTrustedEvent
, eMouseClick
, nullptr,
1059 WidgetMouseEvent::eReal
);
1060 eventDown
.mInputSource
= eventUp
.mInputSource
= eventClick
.mInputSource
=
1064 nsEventStatus status
= nsEventStatus_eIgnore
;
1065 EventDispatcher::Dispatch(static_cast<nsIContent
*>(this), context
,
1066 &eventDown
, nullptr, &status
);
1069 status
= nsEventStatus_eIgnore
; // reset status
1070 EventDispatcher::Dispatch(static_cast<nsIContent
*>(this), context
,
1071 &eventUp
, nullptr, &status
);
1074 status
= nsEventStatus_eIgnore
; // reset status
1075 EventDispatcher::Dispatch(static_cast<nsIContent
*>(this), context
,
1076 &eventClick
, nullptr, &status
);
1078 // If the click has been prevented, lets skip the command call
1079 // this is how a physical click works
1080 if (status
== nsEventStatus_eConsumeNoDefault
) {
1086 // oncommand is fired when an element is clicked...
1090 void nsXULElement::DoCommand() {
1091 nsCOMPtr
<Document
> doc
= GetComposedDoc(); // strong just in case
1093 RefPtr
<nsXULElement
> self
= this;
1094 nsContentUtils::DispatchXULCommand(self
, true);
1098 bool nsXULElement::IsNodeOfType(uint32_t aFlags
) const { return false; }
1100 nsresult
nsXULElement::AddPopupListener(nsAtom
* aName
) {
1101 // Add a popup listener to the element
1103 (aName
== nsGkAtoms::context
|| aName
== nsGkAtoms::contextmenu
);
1104 uint32_t listenerFlag
= isContext
? XUL_ELEMENT_HAS_CONTENTMENU_LISTENER
1105 : XUL_ELEMENT_HAS_POPUP_LISTENER
;
1107 if (HasFlag(listenerFlag
)) {
1111 nsCOMPtr
<nsIDOMEventListener
> listener
=
1112 new nsXULPopupListener(this, isContext
);
1114 // Add the popup as a listener on this element.
1115 EventListenerManager
* manager
= GetOrCreateListenerManager();
1116 SetFlags(listenerFlag
);
1119 manager
->AddEventListenerByType(listener
, u
"contextmenu"_ns
,
1120 TrustedEventsAtSystemGroupBubble());
1122 manager
->AddEventListenerByType(listener
, u
"mousedown"_ns
,
1123 TrustedEventsAtSystemGroupBubble());
1128 //----------------------------------------------------------------------
1130 nsresult
nsXULElement::MakeHeavyweight(nsXULPrototypeElement
* aPrototype
) {
1137 for (i
= 0; i
< aPrototype
->mAttributes
.Length(); ++i
) {
1138 nsXULPrototypeAttribute
* protoattr
= &aPrototype
->mAttributes
[i
];
1139 nsAttrValue attrValue
;
1141 // Style rules need to be cloned.
1142 if (protoattr
->mValue
.Type() == nsAttrValue::eCSSDeclaration
) {
1143 DeclarationBlock
* decl
= protoattr
->mValue
.GetCSSDeclarationValue();
1144 RefPtr
<DeclarationBlock
> declClone
= decl
->Clone();
1146 nsString stringValue
;
1147 protoattr
->mValue
.ToString(stringValue
);
1149 attrValue
.SetTo(declClone
.forget(), &stringValue
);
1151 attrValue
.SetTo(protoattr
->mValue
);
1155 // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName
1156 if (protoattr
->mName
.IsAtom()) {
1157 rv
= mAttrs
.SetAndSwapAttr(protoattr
->mName
.Atom(), attrValue
,
1160 rv
= mAttrs
.SetAndSwapAttr(protoattr
->mName
.NodeInfo(), attrValue
,
1163 NS_ENSURE_SUCCESS(rv
, rv
);
1168 bool nsXULElement::BoolAttrIsTrue(nsAtom
* aName
) const {
1169 const nsAttrValue
* attr
= GetAttrInfo(kNameSpaceID_None
, aName
).mValue
;
1171 return attr
&& attr
->Type() == nsAttrValue::eAtom
&&
1172 attr
->GetAtomValue() == nsGkAtoms::_true
;
1175 void nsXULElement::RecompileScriptEventListeners() {
1176 int32_t i
, count
= mAttrs
.AttrCount();
1177 for (i
= 0; i
< count
; ++i
) {
1178 const nsAttrName
* name
= mAttrs
.AttrNameAt(i
);
1180 // Eventlistenener-attributes are always in the null namespace
1181 if (!name
->IsAtom()) {
1185 nsAtom
* attr
= name
->Atom();
1186 if (!nsContentUtils::IsEventAttributeName(attr
, EventNameType_XUL
)) {
1191 GetAttr(kNameSpaceID_None
, attr
, value
);
1192 SetEventHandler(attr
, value
, true);
1196 bool nsXULElement::IsEventAttributeNameInternal(nsAtom
* aName
) {
1197 return nsContentUtils::IsEventAttributeName(aName
, EventNameType_XUL
);
1200 JSObject
* nsXULElement::WrapNode(JSContext
* aCx
,
1201 JS::Handle
<JSObject
*> aGivenProto
) {
1202 return dom::XULElement_Binding::Wrap(aCx
, this, aGivenProto
);
1205 bool nsXULElement::IsInteractiveHTMLContent() const {
1206 return IsXULElement(nsGkAtoms::menupopup
) ||
1207 Element::IsInteractiveHTMLContent();
1210 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode
)
1212 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode
)
1213 if (tmp
->mType
== nsXULPrototypeNode::eType_Element
) {
1214 static_cast<nsXULPrototypeElement
*>(tmp
)->Unlink();
1216 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1217 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode
)
1218 if (tmp
->mType
== nsXULPrototypeNode::eType_Element
) {
1219 nsXULPrototypeElement
* elem
= static_cast<nsXULPrototypeElement
*>(tmp
);
1220 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mNodeInfo");
1221 cb
.NoteNativeChild(elem
->mNodeInfo
,
1222 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo
));
1224 for (i
= 0; i
< elem
->mAttributes
.Length(); ++i
) {
1225 const nsAttrName
& name
= elem
->mAttributes
[i
].mName
;
1226 if (!name
.IsAtom()) {
1227 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
1228 "mAttributes[i].mName.NodeInfo()");
1229 cb
.NoteNativeChild(name
.NodeInfo(),
1230 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo
));
1233 ImplCycleCollectionTraverse(cb
, elem
->mChildren
, "mChildren");
1235 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1236 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode
)
1237 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1239 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode
, AddRef
)
1240 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode
, Release
)
1242 //----------------------------------------------------------------------
1244 // nsXULPrototypeAttribute
1247 nsXULPrototypeAttribute::~nsXULPrototypeAttribute() {
1248 MOZ_COUNT_DTOR(nsXULPrototypeAttribute
);
1251 //----------------------------------------------------------------------
1253 // nsXULPrototypeElement
1256 nsresult
nsXULPrototypeElement::Serialize(
1257 nsIObjectOutputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
1258 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
1261 // Write basic prototype data
1262 rv
= aStream
->Write32(mType
);
1265 int32_t index
= aNodeInfos
->IndexOf(mNodeInfo
);
1266 NS_ASSERTION(index
>= 0, "unknown mozilla::dom::NodeInfo index");
1267 nsresult tmp
= aStream
->Write32(index
);
1268 if (NS_FAILED(tmp
)) {
1273 tmp
= aStream
->Write32(mAttributes
.Length());
1274 if (NS_FAILED(tmp
)) {
1278 nsAutoString attributeValue
;
1280 for (i
= 0; i
< mAttributes
.Length(); ++i
) {
1281 RefPtr
<mozilla::dom::NodeInfo
> ni
;
1282 if (mAttributes
[i
].mName
.IsAtom()) {
1283 ni
= mNodeInfo
->NodeInfoManager()->GetNodeInfo(
1284 mAttributes
[i
].mName
.Atom(), nullptr, kNameSpaceID_None
,
1285 nsINode::ATTRIBUTE_NODE
);
1286 NS_ASSERTION(ni
, "the nodeinfo should already exist");
1288 ni
= mAttributes
[i
].mName
.NodeInfo();
1291 index
= aNodeInfos
->IndexOf(ni
);
1292 NS_ASSERTION(index
>= 0, "unknown mozilla::dom::NodeInfo index");
1293 tmp
= aStream
->Write32(index
);
1294 if (NS_FAILED(tmp
)) {
1298 mAttributes
[i
].mValue
.ToString(attributeValue
);
1299 tmp
= aStream
->WriteWStringZ(attributeValue
.get());
1300 if (NS_FAILED(tmp
)) {
1305 // Now write children
1306 tmp
= aStream
->Write32(uint32_t(mChildren
.Length()));
1307 if (NS_FAILED(tmp
)) {
1310 for (i
= 0; i
< mChildren
.Length(); i
++) {
1311 nsXULPrototypeNode
* child
= mChildren
[i
].get();
1312 switch (child
->mType
) {
1316 tmp
= child
->Serialize(aStream
, aProtoDoc
, aNodeInfos
);
1317 if (NS_FAILED(tmp
)) {
1322 tmp
= aStream
->Write32(child
->mType
);
1323 if (NS_FAILED(tmp
)) {
1326 nsXULPrototypeScript
* script
=
1327 static_cast<nsXULPrototypeScript
*>(child
);
1329 tmp
= aStream
->Write8(script
->mOutOfLine
);
1330 if (NS_FAILED(tmp
)) {
1333 if (!script
->mOutOfLine
) {
1334 tmp
= script
->Serialize(aStream
, aProtoDoc
, aNodeInfos
);
1335 if (NS_FAILED(tmp
)) {
1339 tmp
= aStream
->WriteCompoundObject(script
->mSrcURI
,
1340 NS_GET_IID(nsIURI
), true);
1341 if (NS_FAILED(tmp
)) {
1345 if (script
->HasStencil()) {
1346 // This may return NS_OK without muxing script->mSrcURI's
1347 // data into the cache file, in the case where that
1348 // muxed document is already there (written by a prior
1349 // session, or by an earlier cache episode during this
1351 tmp
= script
->SerializeOutOfLine(aStream
, aProtoDoc
);
1352 if (NS_FAILED(tmp
)) {
1364 nsresult
nsXULPrototypeElement::Deserialize(
1365 nsIObjectInputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
1366 nsIURI
* aDocumentURI
,
1367 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
1368 MOZ_ASSERT(aNodeInfos
, "missing nodeinfo array");
1371 uint32_t number
= 0;
1372 nsresult rv
= aStream
->Read32(&number
);
1373 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1374 mNodeInfo
= aNodeInfos
->SafeElementAt(number
, nullptr);
1376 return NS_ERROR_UNEXPECTED
;
1380 rv
= aStream
->Read32(&number
);
1381 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1382 int32_t attributes
= int32_t(number
);
1384 if (attributes
> 0) {
1385 mAttributes
.AppendElements(attributes
);
1387 nsAutoString attributeValue
;
1388 for (size_t i
= 0; i
< mAttributes
.Length(); ++i
) {
1389 rv
= aStream
->Read32(&number
);
1390 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1391 mozilla::dom::NodeInfo
* ni
= aNodeInfos
->SafeElementAt(number
, nullptr);
1393 return NS_ERROR_UNEXPECTED
;
1396 mAttributes
[i
].mName
.SetTo(ni
);
1398 rv
= aStream
->ReadString(attributeValue
);
1399 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1400 rv
= SetAttrAt(i
, attributeValue
, aDocumentURI
);
1401 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1405 rv
= aStream
->Read32(&number
);
1406 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1407 uint32_t numChildren
= int32_t(number
);
1409 if (numChildren
> 0) {
1410 if (!mChildren
.SetCapacity(numChildren
, fallible
)) {
1411 return NS_ERROR_OUT_OF_MEMORY
;
1414 for (uint32_t i
= 0; i
< numChildren
; i
++) {
1415 rv
= aStream
->Read32(&number
);
1416 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1417 Type childType
= (Type
)number
;
1419 RefPtr
<nsXULPrototypeNode
> child
;
1421 switch (childType
) {
1423 child
= new nsXULPrototypeElement();
1424 rv
= child
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
, aNodeInfos
);
1425 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1428 child
= new nsXULPrototypeText();
1429 rv
= child
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
, aNodeInfos
);
1430 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1433 child
= new nsXULPrototypePI();
1434 rv
= child
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
, aNodeInfos
);
1435 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1437 case eType_Script
: {
1438 // language version/options obtained during deserialization.
1439 RefPtr
<nsXULPrototypeScript
> script
= new nsXULPrototypeScript(0);
1441 rv
= aStream
->ReadBoolean(&script
->mOutOfLine
);
1442 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1443 if (!script
->mOutOfLine
) {
1444 rv
= script
->Deserialize(aStream
, aProtoDoc
, aDocumentURI
,
1446 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1448 nsCOMPtr
<nsISupports
> supports
;
1449 rv
= aStream
->ReadObject(true, getter_AddRefs(supports
));
1450 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1451 script
->mSrcURI
= do_QueryInterface(supports
);
1453 rv
= script
->DeserializeOutOfLine(aStream
, aProtoDoc
);
1454 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1457 child
= std::move(script
);
1461 MOZ_ASSERT(false, "Unexpected child type!");
1462 return NS_ERROR_UNEXPECTED
;
1465 MOZ_ASSERT(child
, "Don't append null to mChildren");
1466 MOZ_ASSERT(child
->mType
== childType
);
1467 mChildren
.AppendElement(child
);
1469 // Oh dear. Something failed during the deserialization.
1470 // We don't know what. But likely consequences of failed
1471 // deserializations included calls to |AbortCaching| which
1472 // shuts down the cache and closes our streams.
1473 // If that happens, next time through this loop, we die a messy
1474 // death. So, let's just fail now, and propagate that failure
1475 // upward so that the ChromeProtocolHandler knows it can't use
1476 // a cached chrome channel for this.
1477 if (NS_WARN_IF(NS_FAILED(rv
))) return rv
;
1484 nsresult
nsXULPrototypeElement::SetAttrAt(uint32_t aPos
,
1485 const nsAString
& aValue
,
1486 nsIURI
* aDocumentURI
) {
1487 MOZ_ASSERT(aPos
< mAttributes
.Length(), "out-of-bounds");
1490 // This code is largely duplicated in nsXULElement::SetAttr.
1491 // Any changes should be made to both functions.
1493 if (!mNodeInfo
->NamespaceEquals(kNameSpaceID_XUL
)) {
1494 if (mNodeInfo
->NamespaceEquals(kNameSpaceID_XHTML
) &&
1495 mAttributes
[aPos
].mName
.Equals(nsGkAtoms::is
)) {
1496 // We still care about the is attribute set on HTML elements.
1497 mAttributes
[aPos
].mValue
.ParseAtom(aValue
);
1498 mIsAtom
= mAttributes
[aPos
].mValue
.GetAtomValue();
1503 mAttributes
[aPos
].mValue
.ParseStringOrAtom(aValue
);
1508 if (mAttributes
[aPos
].mName
.Equals(nsGkAtoms::id
) && !aValue
.IsEmpty()) {
1509 mHasIdAttribute
= true;
1510 // Store id as atom.
1511 // id="" means that the element has no id. Not that it has
1512 // emptystring as id.
1513 mAttributes
[aPos
].mValue
.ParseAtom(aValue
);
1516 } else if (mAttributes
[aPos
].mName
.Equals(nsGkAtoms::is
)) {
1517 // Store is as atom.
1518 mAttributes
[aPos
].mValue
.ParseAtom(aValue
);
1519 mIsAtom
= mAttributes
[aPos
].mValue
.GetAtomValue();
1522 } else if (mAttributes
[aPos
].mName
.Equals(nsGkAtoms::_class
)) {
1523 mHasClassAttribute
= true;
1524 // Compute the element's class list
1525 mAttributes
[aPos
].mValue
.ParseAtomArray(aValue
);
1528 } else if (mAttributes
[aPos
].mName
.Equals(nsGkAtoms::style
)) {
1529 mHasStyleAttribute
= true;
1530 // Parse the element's 'style' attribute
1532 // This is basically duplicating what nsINode::NodePrincipal() does
1533 nsIPrincipal
* principal
= mNodeInfo
->NodeInfoManager()->DocumentPrincipal();
1534 // XXX Get correct Base URI (need GetBaseURI on *prototype* element)
1535 // TODO: If we implement Content Security Policy for chrome documents
1536 // as has been discussed, the CSP should be checked here to see if
1537 // inline styles are allowed to be applied.
1538 // XXX No specific specs talk about xul and referrer policy, pass Unset
1540 MakeRefPtr
<ReferrerInfo
>(aDocumentURI
, ReferrerPolicy::_empty
);
1541 auto data
= MakeRefPtr
<URLExtraData
>(aDocumentURI
, referrerInfo
, principal
);
1542 RefPtr
<DeclarationBlock
> declaration
= DeclarationBlock::FromCssText(
1543 aValue
, data
, eCompatibility_FullStandards
, nullptr,
1544 StyleCssRuleType::Style
);
1546 mAttributes
[aPos
].mValue
.SetTo(declaration
.forget(), &aValue
);
1550 // Don't abort if parsing failed, it could just be malformed css.
1551 } else if (mAttributes
[aPos
].mName
.Equals(nsGkAtoms::tabindex
)) {
1552 mAttributes
[aPos
].mValue
.ParseIntValue(aValue
);
1557 mAttributes
[aPos
].mValue
.ParseStringOrAtom(aValue
);
1562 void nsXULPrototypeElement::Unlink() {
1563 mAttributes
.Clear();
1567 //----------------------------------------------------------------------
1569 // nsXULPrototypeScript
1572 nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo
)
1573 : nsXULPrototypeNode(eType_Script
),
1577 mSrcLoadWaiters(nullptr),
1578 mStencil(nullptr) {}
1580 static nsresult
WriteStencil(nsIObjectOutputStream
* aStream
, JSContext
* aCx
,
1581 const JS::ReadOnlyCompileOptions
& aOptions
,
1582 JS::Stencil
* aStencil
) {
1583 uint8_t flags
= 0; // We don't have flags anymore.
1584 nsresult rv
= aStream
->Write8(flags
);
1585 if (NS_FAILED(rv
)) {
1589 JS::TranscodeBuffer buffer
;
1590 JS::TranscodeResult code
;
1591 code
= JS::EncodeStencil(aCx
, aOptions
, aStencil
, buffer
);
1593 if (code
!= JS::TranscodeResult::Ok
) {
1594 if (code
== JS::TranscodeResult::Throw
) {
1595 JS_ClearPendingException(aCx
);
1596 return NS_ERROR_OUT_OF_MEMORY
;
1599 MOZ_ASSERT(IsTranscodeFailureResult(code
));
1600 return NS_ERROR_FAILURE
;
1603 size_t size
= buffer
.length();
1604 if (size
> UINT32_MAX
) {
1605 return NS_ERROR_FAILURE
;
1607 rv
= aStream
->Write32(size
);
1608 if (NS_SUCCEEDED(rv
)) {
1609 // Ideally we could just pass "buffer" here. See bug 1566574.
1610 rv
= aStream
->WriteBytes(Span(buffer
.begin(), size
));
1616 static nsresult
ReadStencil(nsIObjectInputStream
* aStream
, JSContext
* aCx
,
1617 const JS::ReadOnlyCompileOptions
& aOptions
,
1618 JS::Stencil
** aStencilOut
) {
1620 nsresult rv
= aStream
->Read8(&flags
);
1621 if (NS_FAILED(rv
)) {
1625 // We don't serialize mutedError-ness of scripts, which is fine as long as
1626 // we only serialize system and XUL-y things. We can detect this by checking
1627 // where the caller wants us to deserialize.
1629 // CompilationScope() could theoretically GC, so get that out of the way
1630 // before comparing to the cx global.
1631 JSObject
* loaderGlobal
= xpc::CompilationScope();
1632 MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(aCx
) ||
1633 JS::CurrentGlobalOrNull(aCx
) == loaderGlobal
);
1636 rv
= aStream
->Read32(&size
);
1637 if (NS_FAILED(rv
)) {
1642 rv
= aStream
->ReadBytes(size
, &data
);
1643 if (NS_FAILED(rv
)) {
1647 // The decoded stencil shouldn't borrow from the XDR buffer.
1648 MOZ_ASSERT(!aOptions
.borrowBuffer
);
1649 auto cleanupData
= MakeScopeExit([&]() { free(data
); });
1651 JS::TranscodeRange
range(reinterpret_cast<uint8_t*>(data
), size
);
1654 JS::TranscodeResult code
;
1655 RefPtr
<JS::Stencil
> stencil
;
1656 code
= JS::DecodeStencil(aCx
, aOptions
, range
, getter_AddRefs(stencil
));
1657 if (code
!= JS::TranscodeResult::Ok
) {
1658 if (code
== JS::TranscodeResult::Throw
) {
1659 JS_ClearPendingException(aCx
);
1660 return NS_ERROR_OUT_OF_MEMORY
;
1663 MOZ_ASSERT(IsTranscodeFailureResult(code
));
1664 return NS_ERROR_FAILURE
;
1667 stencil
.forget(aStencilOut
);
1673 void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions
& options
) {
1674 // If the script was inline, tell the JS parser to save source for
1675 // Function.prototype.toSource(). If it's out of line, we retrieve the
1676 // source from the files on demand.
1677 options
.setSourceIsLazy(mOutOfLine
);
1680 nsresult
nsXULPrototypeScript::Serialize(
1681 nsIObjectOutputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
1682 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
1683 NS_ENSURE_TRUE(aProtoDoc
, NS_ERROR_UNEXPECTED
);
1686 if (!jsapi
.Init(xpc::CompilationScope())) {
1687 return NS_ERROR_UNEXPECTED
;
1690 NS_ASSERTION(!mSrcLoading
|| mSrcLoadWaiters
!= nullptr || !mStencil
,
1691 "script source still loading when serializing?!");
1692 if (!mStencil
) return NS_ERROR_FAILURE
;
1694 // Write basic prototype data
1696 rv
= aStream
->Write32(mLineNo
);
1697 if (NS_FAILED(rv
)) return rv
;
1698 rv
= aStream
->Write32(0); // See bug 1418294.
1699 if (NS_FAILED(rv
)) return rv
;
1701 JSContext
* cx
= jsapi
.cx();
1702 MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx
));
1704 JS::CompileOptions
options(cx
);
1705 FillCompileOptions(options
);
1707 return WriteStencil(aStream
, cx
, options
, mStencil
);
1710 nsresult
nsXULPrototypeScript::SerializeOutOfLine(
1711 nsIObjectOutputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
) {
1712 if (!mSrcURI
->SchemeIs("chrome"))
1713 // Don't cache scripts that don't come from chrome uris.
1714 return NS_ERROR_NOT_IMPLEMENTED
;
1716 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1717 if (!cache
) return NS_ERROR_OUT_OF_MEMORY
;
1719 NS_ASSERTION(cache
->IsEnabled(),
1720 "writing to the cache file, but the XUL cache is off?");
1722 cache
->HasData(mSrcURI
, &exists
);
1724 /* return will be NS_OK from GetAsciiSpec.
1725 * that makes no sense.
1726 * nor does returning NS_OK from HasMuxedDocument.
1727 * XXX return something meaningful.
1729 if (exists
) return NS_OK
;
1731 nsCOMPtr
<nsIObjectOutputStream
> oos
;
1732 nsresult rv
= cache
->GetOutputStream(mSrcURI
, getter_AddRefs(oos
));
1733 NS_ENSURE_SUCCESS(rv
, rv
);
1735 nsresult tmp
= Serialize(oos
, aProtoDoc
, nullptr);
1736 if (NS_FAILED(tmp
)) {
1739 tmp
= cache
->FinishOutputStream(mSrcURI
);
1740 if (NS_FAILED(tmp
)) {
1744 if (NS_FAILED(rv
)) cache
->AbortCaching();
1748 nsresult
nsXULPrototypeScript::Deserialize(
1749 nsIObjectInputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
1750 nsIURI
* aDocumentURI
,
1751 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
1753 NS_ASSERTION(!mSrcLoading
|| mSrcLoadWaiters
!= nullptr || !mStencil
,
1754 "prototype script not well-initialized when deserializing?!");
1756 // Read basic prototype data
1757 rv
= aStream
->Read32(&mLineNo
);
1758 if (NS_FAILED(rv
)) return rv
;
1760 rv
= aStream
->Read32(&dummy
); // See bug 1418294.
1761 if (NS_FAILED(rv
)) return rv
;
1764 if (!jsapi
.Init(xpc::CompilationScope())) {
1765 return NS_ERROR_UNEXPECTED
;
1767 JSContext
* cx
= jsapi
.cx();
1769 JS::CompileOptions
options(cx
);
1770 FillCompileOptions(options
);
1772 RefPtr
<JS::Stencil
> newStencil
;
1773 rv
= ReadStencil(aStream
, cx
, options
, getter_AddRefs(newStencil
));
1774 NS_ENSURE_SUCCESS(rv
, rv
);
1779 nsresult
nsXULPrototypeScript::DeserializeOutOfLine(
1780 nsIObjectInputStream
* aInput
, nsXULPrototypeDocument
* aProtoDoc
) {
1781 // Keep track of failure via rv, so we can
1782 // AbortCaching if things look bad.
1783 nsresult rv
= NS_OK
;
1784 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1786 nsCOMPtr
<nsIObjectInputStream
> objectInput
= aInput
;
1788 bool useXULCache
= true;
1790 // NB: we must check the XUL script cache early, to avoid
1791 // multiple deserialization attempts for a given script.
1792 // Note that PrototypeDocumentContentSink::LoadScript
1793 // checks the XUL script cache too, in order to handle the
1794 // serialization case.
1796 // We need do this only for <script src='strres.js'> and the
1797 // like, i.e., out-of-line scripts that are included by several
1798 // different XUL documents stored in the cache file.
1799 useXULCache
= cache
->IsEnabled();
1802 RefPtr
<JS::Stencil
> newStencil
= cache
->GetStencil(mSrcURI
);
1811 rv
= cache
->GetInputStream(mSrcURI
, getter_AddRefs(objectInput
));
1813 // If !mSrcURI, we have an inline script. We shouldn't have
1814 // to do anything else in that case, I think.
1816 // We do reflect errors into rv, but our caller may want to
1817 // ignore our return value, because mStencil will be null
1818 // after any error, and that suffices to cause the script to
1819 // be reloaded (from the src= URI, if any) and recompiled.
1820 // We're better off slow-loading than bailing out due to a
1822 if (NS_SUCCEEDED(rv
))
1823 rv
= Deserialize(objectInput
, aProtoDoc
, nullptr, nullptr);
1825 if (NS_SUCCEEDED(rv
)) {
1826 if (useXULCache
&& mSrcURI
&& mSrcURI
->SchemeIs("chrome")) {
1827 cache
->PutStencil(mSrcURI
, GetStencil());
1829 cache
->FinishInputStream(mSrcURI
);
1831 // If mSrcURI is not in the cache,
1832 // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
1833 // update the cache file to hold a serialization of
1834 // this script, once it has finished loading.
1835 if (rv
!= NS_ERROR_NOT_AVAILABLE
) cache
->AbortCaching();
1842 class NotifyOffThreadScriptCompletedRunnable
: public Runnable
{
1843 // An array of all outstanding script receivers. All reference counting of
1844 // these objects happens on the main thread. When we return to the main
1845 // thread from script compilation we make sure our receiver is still in
1846 // this array (still alive) before proceeding. This array is cleared during
1847 // shutdown, potentially before all outstanding script compilations have
1848 // finished. We do not need to worry about pointer replay here, because
1849 // a) we should not be starting script compilation after clearing this
1850 // array and b) in all other cases the receiver will still be alive.
1851 static StaticAutoPtr
<nsTArray
<nsCOMPtr
<nsIOffThreadScriptReceiver
>>>
1853 static bool sSetupClearOnShutdown
;
1855 nsIOffThreadScriptReceiver
* mReceiver
;
1856 JS::OffThreadToken
* mToken
;
1859 NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver
* aReceiver
,
1860 JS::OffThreadToken
* aToken
)
1861 : mozilla::Runnable("NotifyOffThreadScriptCompletedRunnable"),
1862 mReceiver(aReceiver
),
1865 static void NoteReceiver(nsIOffThreadScriptReceiver
* aReceiver
) {
1866 if (!sSetupClearOnShutdown
) {
1867 ClearOnShutdown(&sReceivers
);
1868 sSetupClearOnShutdown
= true;
1869 sReceivers
= new nsTArray
<nsCOMPtr
<nsIOffThreadScriptReceiver
>>();
1872 // If we ever crash here, it's because we tried to lazy compile script
1873 // too late in shutdown.
1874 sReceivers
->AppendElement(aReceiver
);
1880 StaticAutoPtr
<nsTArray
<nsCOMPtr
<nsIOffThreadScriptReceiver
>>>
1881 NotifyOffThreadScriptCompletedRunnable::sReceivers
;
1882 bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown
= false;
1885 NotifyOffThreadScriptCompletedRunnable::Run() {
1886 MOZ_ASSERT(NS_IsMainThread());
1888 RefPtr
<JS::Stencil
> stencil
;
1891 if (!jsapi
.Init(xpc::CompilationScope())) {
1892 // Now what? I guess we just leak... this should probably never
1894 return NS_ERROR_UNEXPECTED
;
1896 JSContext
* cx
= jsapi
.cx();
1897 stencil
= JS::FinishOffThreadCompileToStencil(cx
, mToken
);
1901 // We've already shut down.
1905 auto index
= sReceivers
->IndexOf(mReceiver
);
1906 MOZ_RELEASE_ASSERT(index
!= sReceivers
->NoIndex
);
1907 nsCOMPtr
<nsIOffThreadScriptReceiver
> receiver
=
1908 std::move((*sReceivers
)[index
]);
1909 sReceivers
->RemoveElementAt(index
);
1911 return receiver
->OnScriptCompileComplete(stencil
,
1912 stencil
? NS_OK
: NS_ERROR_FAILURE
);
1915 static void OffThreadScriptReceiverCallback(JS::OffThreadToken
* aToken
,
1916 void* aCallbackData
) {
1917 // Be careful not to adjust the refcount on the receiver, as this callback
1918 // may be invoked off the main thread.
1919 nsIOffThreadScriptReceiver
* aReceiver
=
1920 static_cast<nsIOffThreadScriptReceiver
*>(aCallbackData
);
1921 RefPtr
<NotifyOffThreadScriptCompletedRunnable
> notify
=
1922 new NotifyOffThreadScriptCompletedRunnable(aReceiver
, aToken
);
1923 NS_DispatchToMainThread(notify
);
1926 nsresult
nsXULPrototypeScript::Compile(
1927 const char16_t
* aText
, size_t aTextLength
, JS::SourceOwnership aOwnership
,
1928 nsIURI
* aURI
, uint32_t aLineNo
, Document
* aDocument
,
1929 nsIOffThreadScriptReceiver
* aOffThreadReceiver
/* = nullptr */) {
1930 // We'll compile the script in the compilation scope.
1932 if (!jsapi
.Init(xpc::CompilationScope())) {
1933 if (aOwnership
== JS::SourceOwnership::TakeOwnership
) {
1934 // In this early-exit case -- before the |srcBuf.init| call will
1935 // own |aText| -- we must relinquish ownership manually.
1936 js_free(const_cast<char16_t
*>(aText
));
1939 return NS_ERROR_UNEXPECTED
;
1941 JSContext
* cx
= jsapi
.cx();
1943 JS::SourceText
<char16_t
> srcBuf
;
1944 if (NS_WARN_IF(!srcBuf
.init(cx
, aText
, aTextLength
, aOwnership
))) {
1945 return NS_ERROR_FAILURE
;
1948 nsAutoCString urlspec
;
1949 nsresult rv
= aURI
->GetSpec(urlspec
);
1950 if (NS_WARN_IF(NS_FAILED(rv
))) {
1954 // Ok, compile it to create a prototype script object!
1955 JS::CompileOptions
options(cx
);
1956 FillCompileOptions(options
);
1957 options
.setIntroductionType(mOutOfLine
? "srcScript" : "inlineScript")
1958 .setFileAndLine(urlspec
.get(), mOutOfLine
? 1 : aLineNo
);
1960 JS::Rooted
<JSObject
*> scope(cx
, JS::CurrentGlobalOrNull(cx
));
1962 if (aOffThreadReceiver
&& JS::CanCompileOffThread(cx
, options
, aTextLength
)) {
1963 if (!JS::CompileToStencilOffThread(
1964 cx
, options
, srcBuf
, OffThreadScriptReceiverCallback
,
1965 static_cast<void*>(aOffThreadReceiver
))) {
1966 JS_ClearPendingException(cx
);
1967 return NS_ERROR_OUT_OF_MEMORY
;
1969 NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver
);
1971 RefPtr
<JS::Stencil
> stencil
=
1972 JS::CompileGlobalScriptToStencil(cx
, options
, srcBuf
);
1974 return NS_ERROR_OUT_OF_MEMORY
;
1981 nsresult
nsXULPrototypeScript::InstantiateScript(
1982 JSContext
* aCx
, JS::MutableHandleScript aScript
) {
1983 MOZ_ASSERT(mStencil
);
1985 JS::CompileOptions
options(aCx
);
1986 FillCompileOptions(options
);
1987 // We don't need setIntroductionType and setFileAndLine here, unlike
1988 // nsXULPrototypeScript::Compile.
1989 // mStencil already contains the information.
1990 aScript
.set(JS::InstantiateGlobalStencil(aCx
, options
, mStencil
));
1992 JS_ClearPendingException(aCx
);
1993 return NS_ERROR_OUT_OF_MEMORY
;
1999 void nsXULPrototypeScript::Set(JS::Stencil
* aStencil
) { mStencil
= aStencil
; }
2001 //----------------------------------------------------------------------
2003 // nsXULPrototypeText
2006 nsresult
nsXULPrototypeText::Serialize(
2007 nsIObjectOutputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
2008 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
2011 // Write basic prototype data
2012 rv
= aStream
->Write32(mType
);
2014 nsresult tmp
= aStream
->WriteWStringZ(mValue
.get());
2015 if (NS_FAILED(tmp
)) {
2022 nsresult
nsXULPrototypeText::Deserialize(
2023 nsIObjectInputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
2024 nsIURI
* aDocumentURI
,
2025 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
2026 nsresult rv
= aStream
->ReadString(mValue
);
2027 if (NS_WARN_IF(NS_FAILED(rv
))) {
2033 //----------------------------------------------------------------------
2038 nsresult
nsXULPrototypePI::Serialize(
2039 nsIObjectOutputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
2040 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
2043 // Write basic prototype data
2044 rv
= aStream
->Write32(mType
);
2046 nsresult tmp
= aStream
->WriteWStringZ(mTarget
.get());
2047 if (NS_FAILED(tmp
)) {
2050 tmp
= aStream
->WriteWStringZ(mData
.get());
2051 if (NS_FAILED(tmp
)) {
2058 nsresult
nsXULPrototypePI::Deserialize(
2059 nsIObjectInputStream
* aStream
, nsXULPrototypeDocument
* aProtoDoc
,
2060 nsIURI
* aDocumentURI
,
2061 const nsTArray
<RefPtr
<mozilla::dom::NodeInfo
>>* aNodeInfos
) {
2064 rv
= aStream
->ReadString(mTarget
);
2065 if (NS_FAILED(rv
)) return rv
;
2066 rv
= aStream
->ReadString(mData
);
2067 if (NS_FAILED(rv
)) return rv
;