Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsCOMPtr.h"
7
#include "nsDOMCID.h"
8
#include "nsError.h"
9
#include "nsDOMString.h"
10
#include "nsAtom.h"
11
#include "nsIBaseWindow.h"
12
#include "nsIDOMEventListener.h"
13
#include "nsIDOMXULControlElement.h"
14
#include "nsIDOMXULSelectCntrlItemEl.h"
15
#include "mozilla/dom/BindContext.h"
16
#include "mozilla/dom/Document.h"
17
#include "mozilla/ClearOnShutdown.h"
18
#include "mozilla/EventListenerManager.h"
19
#include "mozilla/EventStateManager.h"
20
#include "mozilla/EventStates.h"
21
#include "mozilla/DeclarationBlock.h"
22
#include "mozilla/PresShell.h"
23
#include "js/CompilationAndEvaluation.h"
24
#include "js/SourceText.h"
25
#include "nsFocusManager.h"
26
#include "nsHTMLStyleSheet.h"
27
#include "nsNameSpaceManager.h"
28
#include "nsIObjectInputStream.h"
29
#include "nsIObjectOutputStream.h"
30
#include "nsIPrincipal.h"
31
#include "nsIScriptContext.h"
32
#include "nsIXPConnect.h"
33
#include "nsViewManager.h"
34
#include "nsIWidget.h"
35
#include "nsLayoutCID.h"
36
#include "nsContentCID.h"
37
#include "mozilla/dom/Event.h"
38
#include "nsStyleConsts.h"
39
#include "nsString.h"
40
#include "nsXULControllers.h"
41
#include "nsXULPopupListener.h"
42
#include "nsContentUtils.h"
43
#include "nsContentList.h"
44
#include "mozilla/InternalMutationEvent.h"
45
#include "mozilla/MouseEvents.h"
46
#include "nsPIDOMWindow.h"
47
#include "nsJSPrincipals.h"
48
#include "nsDOMAttributeMap.h"
49
#include "nsGkAtoms.h"
50
#include "nsFrameLoader.h"
51
#include "mozilla/Logging.h"
52
#include "nsIControllers.h"
53
#include "nsAttrValueOrString.h"
54
#include "nsAttrValueInlines.h"
55
#include "mozilla/Attributes.h"
56
#include "nsIController.h"
57
#include "nsQueryObject.h"
58
#include <algorithm>
59
60
#include "nsReadableUtils.h"
61
#include "nsIFrame.h"
62
#include "nsNodeInfoManager.h"
63
#include "nsXULTooltipListener.h"
64
#include "mozilla/EventDispatcher.h"
65
#include "mozAutoDocUpdate.h"
66
#include "nsCCUncollectableMarker.h"
67
#include "nsICSSDeclaration.h"
68
#include "nsLayoutUtils.h"
69
#include "XULFrameElement.h"
70
#include "XULMenuElement.h"
71
#include "XULPopupElement.h"
72
#include "XULTreeElement.h"
73
#include "nsXULPrototypeCache.h"
74
75
#include "mozilla/dom/nsCSPUtils.h"
76
#include "mozilla/dom/XULElementBinding.h"
77
#include "mozilla/dom/XULBroadcastManager.h"
78
#include "mozilla/dom/MouseEventBinding.h"
79
#include "mozilla/dom/MutationEventBinding.h"
80
#include "mozilla/dom/XULCommandEvent.h"
81
#include "mozilla/GlobalKeyListener.h"
82
83
using namespace mozilla;
84
using namespace mozilla::dom;
85
86
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
87
uint32_t nsXULPrototypeAttribute::gNumElements;
88
uint32_t nsXULPrototypeAttribute::gNumAttributes;
89
uint32_t nsXULPrototypeAttribute::gNumCacheTests;
90
uint32_t nsXULPrototypeAttribute::gNumCacheHits;
91
uint32_t nsXULPrototypeAttribute::gNumCacheSets;
92
uint32_t nsXULPrototypeAttribute::gNumCacheFills;
93
#endif
94
95
#define NS_DISPATCH_XUL_COMMAND (1 << 0)
96
97
//----------------------------------------------------------------------
98
// nsXULElement
99
//
100
101
nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
102
: nsStyledElement(std::move(aNodeInfo)) {
103
XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
104
}
105
106
nsXULElement::~nsXULElement() {}
107
108
void nsXULElement::MaybeUpdatePrivateLifetime() {
109
if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype,
110
NS_LITERAL_STRING("navigator:browser"), eCaseMatters) ||
111
AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype,
112
NS_LITERAL_STRING("navigator:geckoview"), eCaseMatters)) {
113
return;
114
}
115
116
nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
117
nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr;
118
if (docShell) {
119
docShell->SetAffectPrivateSessionLifetime(false);
120
}
121
}
122
123
/* static */
124
nsXULElement* NS_NewBasicXULElement(
125
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
126
return new nsXULElement(std::move(aNodeInfo));
127
}
128
129
/* static */
130
nsXULElement* nsXULElement::Construct(
131
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
132
RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
133
if (nodeInfo->Equals(nsGkAtoms::label) ||
134
nodeInfo->Equals(nsGkAtoms::description)) {
135
return new XULTextElement(nodeInfo.forget());
136
}
137
138
if (nodeInfo->Equals(nsGkAtoms::menupopup) ||
139
nodeInfo->Equals(nsGkAtoms::popup) ||
140
nodeInfo->Equals(nsGkAtoms::panel)) {
141
return NS_NewXULPopupElement(nodeInfo.forget());
142
}
143
144
if (nodeInfo->Equals(nsGkAtoms::tooltip)) {
145
return NS_NewXULTooltipElement(nodeInfo.forget());
146
}
147
148
if (nodeInfo->Equals(nsGkAtoms::iframe) ||
149
nodeInfo->Equals(nsGkAtoms::browser) ||
150
nodeInfo->Equals(nsGkAtoms::editor)) {
151
return new XULFrameElement(nodeInfo.forget());
152
}
153
154
if (nodeInfo->Equals(nsGkAtoms::menu) ||
155
nodeInfo->Equals(nsGkAtoms::menulist)) {
156
return new XULMenuElement(nodeInfo.forget());
157
}
158
159
if (nodeInfo->Equals(nsGkAtoms::tree)) {
160
return new XULTreeElement(nodeInfo.forget());
161
}
162
163
return NS_NewBasicXULElement(nodeInfo.forget());
164
}
165
166
/* static */
167
already_AddRefed<nsXULElement> nsXULElement::CreateFromPrototype(
168
nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo* aNodeInfo,
169
bool aIsScriptable, bool aIsRoot) {
170
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
171
nsCOMPtr<Element> baseElement;
172
NS_NewXULElement(getter_AddRefs(baseElement), ni.forget(),
173
dom::FROM_PARSER_NETWORK, aPrototype->mIsAtom);
174
175
if (baseElement) {
176
nsXULElement* element = FromNode(baseElement);
177
178
if (aPrototype->mHasIdAttribute) {
179
element->SetHasID();
180
}
181
if (aPrototype->mHasClassAttribute) {
182
element->SetMayHaveClass();
183
}
184
if (aPrototype->mHasStyleAttribute) {
185
element->SetMayHaveStyle();
186
}
187
188
element->MakeHeavyweight(aPrototype);
189
if (aIsScriptable) {
190
// Check each attribute on the prototype to see if we need to do
191
// any additional processing and hookup that would otherwise be
192
// done 'automagically' by SetAttr().
193
for (const auto& attribute : aPrototype->mAttributes) {
194
element->AddListenerForAttributeIfNeeded(attribute.mName);
195
}
196
}
197
198
if (aIsRoot && aPrototype->mNodeInfo->Equals(nsGkAtoms::window)) {
199
for (size_t i = 0; i < aPrototype->mAttributes.Length(); ++i) {
200
if (aPrototype->mAttributes[i].mName.Equals(nsGkAtoms::windowtype)) {
201
element->MaybeUpdatePrivateLifetime();
202
}
203
}
204
}
205
206
return baseElement.forget().downcast<nsXULElement>();
207
}
208
209
return nullptr;
210
}
211
212
nsresult nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
213
Document* aDocument,
214
bool aIsScriptable, bool aIsRoot,
215
Element** aResult) {
216
// Create an nsXULElement from a prototype
217
MOZ_ASSERT(aPrototype != nullptr, "null ptr");
218
if (!aPrototype) return NS_ERROR_NULL_POINTER;
219
220
MOZ_ASSERT(aResult != nullptr, "null ptr");
221
if (!aResult) return NS_ERROR_NULL_POINTER;
222
223
RefPtr<mozilla::dom::NodeInfo> nodeInfo;
224
if (aDocument) {
225
mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo;
226
nodeInfo = aDocument->NodeInfoManager()->GetNodeInfo(
227
ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(), ELEMENT_NODE);
228
} else {
229
nodeInfo = aPrototype->mNodeInfo;
230
}
231
232
RefPtr<nsXULElement> element =
233
CreateFromPrototype(aPrototype, nodeInfo, aIsScriptable, aIsRoot);
234
element.forget(aResult);
235
236
return NS_OK;
237
}
238
239
nsresult NS_NewXULElement(Element** aResult,
240
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
241
FromParser aFromParser, nsAtom* aIsAtom,
242
mozilla::dom::CustomElementDefinition* aDefinition) {
243
RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
244
245
MOZ_ASSERT(nodeInfo, "need nodeinfo for non-proto Create");
246
247
NS_ASSERTION(
248
nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
249
"Trying to create XUL elements that don't have the XUL namespace");
250
251
Document* doc = nodeInfo->GetDocument();
252
if (doc && !doc->AllowXULXBL()) {
253
return NS_ERROR_NOT_AVAILABLE;
254
}
255
256
return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser,
257
aIsAtom, aDefinition);
258
}
259
260
void NS_TrustedNewXULElement(
261
Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
262
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
263
MOZ_ASSERT(ni, "need nodeinfo for non-proto Create");
264
265
// Create an nsXULElement with the specified namespace and tag.
266
NS_ADDREF(*aResult = nsXULElement::Construct(ni.forget()));
267
}
268
269
//----------------------------------------------------------------------
270
// nsISupports interface
271
272
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULElement, nsStyledElement)
273
274
NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement)
275
NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement)
276
277
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement)
278
NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
279
NS_INTERFACE_MAP_END_INHERITING(nsStyledElement)
280
281
//----------------------------------------------------------------------
282
// nsINode interface
283
284
nsresult nsXULElement::Clone(mozilla::dom::NodeInfo* aNodeInfo,
285
nsINode** aResult) const {
286
*aResult = nullptr;
287
288
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
289
RefPtr<nsXULElement> element = Construct(ni.forget());
290
291
nsresult rv = const_cast<nsXULElement*>(this)->CopyInnerTo(
292
element, ReparseAttributes::No);
293
NS_ENSURE_SUCCESS(rv, rv);
294
295
// Note that we're _not_ copying mControllers.
296
297
element.forget(aResult);
298
return rv;
299
}
300
301
//----------------------------------------------------------------------
302
303
EventListenerManager* nsXULElement::GetEventListenerManagerForAttr(
304
nsAtom* aAttrName, bool* aDefer) {
305
// XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc()
306
// here, override BindToTree for those classes and munge event
307
// listeners there?
308
Document* doc = OwnerDoc();
309
310
nsPIDOMWindowInner* window;
311
Element* root = doc->GetRootElement();
312
if ((!root || root == this) && (window = doc->GetInnerWindow())) {
313
nsCOMPtr<EventTarget> piTarget = do_QueryInterface(window);
314
315
*aDefer = false;
316
return piTarget->GetOrCreateListenerManager();
317
}
318
319
return nsStyledElement::GetEventListenerManagerForAttr(aAttrName, aDefer);
320
}
321
322
// returns true if the element is not a list
323
static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo) {
324
return !aNodeInfo->Equals(nsGkAtoms::tree) &&
325
!aNodeInfo->Equals(nsGkAtoms::richlistbox);
326
}
327
328
bool nsXULElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
329
/*
330
* Returns true if an element may be focused, and false otherwise. The inout
331
* argument aTabIndex will be set to the tab order index to be used; -1 for
332
* elements that should not be part of the tab order and a greater value to
333
* indicate its tab order.
334
*
335
* Confusingly, the supplied value for the aTabIndex argument may indicate
336
* whether the element may be focused as a result of the -moz-user-focus
337
* property, where -1 means no and 0 means yes.
338
*
339
* For controls, the element cannot be focused and is not part of the tab
340
* order if it is disabled.
341
*
342
* -moz-user-focus is overridden if a tabindex (even -1) is specified.
343
*
344
* Specifically, the behaviour for all XUL elements is as follows:
345
* *aTabIndex = -1 no tabindex Not focusable or tabbable
346
* *aTabIndex = -1 tabindex="-1" Focusable but not tabbable
347
* *aTabIndex = -1 tabindex=">=0" Focusable and tabbable
348
* *aTabIndex >= 0 no tabindex Focusable and tabbable
349
* *aTabIndex >= 0 tabindex="-1" Focusable but not tabbable
350
* *aTabIndex >= 0 tabindex=">=0" Focusable and tabbable
351
*
352
* If aTabIndex is null, then the tabindex is not computed, and
353
* true is returned for non-disabled controls and false otherwise.
354
*/
355
356
// elements are not focusable by default
357
bool shouldFocus = false;
358
359
#ifdef XP_MACOSX
360
// on Mac, mouse interactions only focus the element if it's a list,
361
// or if it's a remote target, since the remote target must handle
362
// the focus.
363
if (aWithMouse && IsNonList(mNodeInfo) &&
364
!EventStateManager::IsRemoteTarget(this)) {
365
return false;
366
}
367
#endif
368
369
nsCOMPtr<nsIDOMXULControlElement> xulControl = AsXULControl();
370
if (xulControl) {
371
// a disabled element cannot be focused and is not part of the tab order
372
bool disabled;
373
xulControl->GetDisabled(&disabled);
374
if (disabled) {
375
if (aTabIndex) *aTabIndex = -1;
376
return false;
377
}
378
shouldFocus = true;
379
}
380
381
if (aTabIndex) {
382
if (HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
383
// The tabindex attribute was specified, so the element becomes
384
// focusable.
385
shouldFocus = true;
386
*aTabIndex = TabIndex();
387
} else {
388
// otherwise, if there is no tabindex attribute, just use the value of
389
// *aTabIndex to indicate focusability. Reset any supplied tabindex to 0.
390
shouldFocus = *aTabIndex >= 0;
391
if (shouldFocus) {
392
*aTabIndex = 0;
393
}
394
}
395
396
if (xulControl && shouldFocus && sTabFocusModelAppliesToXUL &&
397
!(sTabFocusModel & eTabFocus_formElementsMask)) {
398
// By default, the tab focus model doesn't apply to xul element on any
399
// system but OS X. on OS X we're following it for UI elements (XUL) as
400
// sTabFocusModel is based on "Full Keyboard Access" system setting (see
401
// mac/nsILookAndFeel). both textboxes and list elements (i.e. trees and
402
// list) should always be focusable (textboxes are handled as html:input)
403
// For compatibility, we only do this for controls, otherwise elements
404
// like <browser> cannot take this focus.
405
if (IsNonList(mNodeInfo)) {
406
*aTabIndex = -1;
407
}
408
}
409
}
410
411
return shouldFocus;
412
}
413
414
int32_t nsXULElement::ScreenX() {
415
nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
416
return frame ? frame->GetScreenRect().x : 0;
417
}
418
419
int32_t nsXULElement::ScreenY() {
420
nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
421
return frame ? frame->GetScreenRect().y : 0;
422
}
423
424
bool nsXULElement::HasMenu() {
425
nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
426
return menu != nullptr;
427
}
428
429
void nsXULElement::OpenMenu(bool aOpenFlag) {
430
nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame(FlushType::Frames));
431
432
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
433
if (pm) {
434
if (aOpenFlag) {
435
// Nothing will happen if this element isn't a menu.
436
pm->ShowMenu(this, false, false);
437
} else if (menu) {
438
nsMenuPopupFrame* popupFrame = menu->GetPopup();
439
if (popupFrame) {
440
pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
441
}
442
}
443
}
444
}
445
446
bool nsXULElement::PerformAccesskey(bool aKeyCausesActivation,
447
bool aIsTrustedEvent) {
448
RefPtr<Element> content(this);
449
450
if (IsXULElement(nsGkAtoms::label)) {
451
nsAutoString control;
452
GetAttr(kNameSpaceID_None, nsGkAtoms::control, control);
453
if (control.IsEmpty()) {
454
return false;
455
}
456
457
// XXXsmaug Should we use ShadowRoot::GetElementById in case
458
// content is in Shadow DOM?
459
nsCOMPtr<Document> document = content->GetUncomposedDoc();
460
if (!document) {
461
return false;
462
}
463
464
content = document->GetElementById(control);
465
if (!content) {
466
return false;
467
}
468
}
469
470
nsIFrame* frame = content->GetPrimaryFrame();
471
if (!frame || !frame->IsVisibleConsideringAncestors()) {
472
return false;
473
}
474
475
bool focused = false;
476
nsXULElement* elm = FromNode(content);
477
if (elm) {
478
// Define behavior for each type of XUL element.
479
if (!content->IsXULElement(nsGkAtoms::toolbarbutton)) {
480
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
481
if (fm) {
482
nsCOMPtr<Element> elementToFocus;
483
// for radio buttons, focus the radiogroup instead
484
if (content->IsXULElement(nsGkAtoms::radio)) {
485
nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem =
486
content->AsXULSelectControlItem();
487
if (controlItem) {
488
bool disabled;
489
controlItem->GetDisabled(&disabled);
490
if (!disabled) {
491
controlItem->GetControl(getter_AddRefs(elementToFocus));
492
}
493
}
494
} else {
495
elementToFocus = content;
496
}
497
if (elementToFocus) {
498
fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY);
499
500
// Return true if the element became focused.
501
nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
502
focused = (window && window->GetFocusedElement());
503
}
504
}
505
}
506
if (aKeyCausesActivation && !content->IsXULElement(nsGkAtoms::menulist)) {
507
elm->ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD,
508
aIsTrustedEvent);
509
}
510
} else {
511
return content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
512
}
513
514
return focused;
515
}
516
517
//----------------------------------------------------------------------
518
519
void nsXULElement::AddListenerForAttributeIfNeeded(nsAtom* aLocalName) {
520
// If appropriate, add a popup listener and/or compile the event
521
// handler. Called when we change the element's document, create a
522
// new element, change an attribute's value, etc.
523
// Eventlistenener-attributes are always in the null namespace.
524
if (aLocalName == nsGkAtoms::menu || aLocalName == nsGkAtoms::contextmenu ||
525
// XXXdwh popup and context are deprecated
526
aLocalName == nsGkAtoms::popup || aLocalName == nsGkAtoms::context) {
527
AddPopupListener(aLocalName);
528
}
529
if (nsContentUtils::IsEventAttributeName(aLocalName, EventNameType_XUL)) {
530
nsAutoString value;
531
GetAttr(kNameSpaceID_None, aLocalName, value);
532
SetEventHandler(aLocalName, value, true);
533
}
534
}
535
536
void nsXULElement::AddListenerForAttributeIfNeeded(const nsAttrName& aName) {
537
if (aName.IsAtom()) {
538
AddListenerForAttributeIfNeeded(aName.Atom());
539
}
540
}
541
542
//----------------------------------------------------------------------
543
//
544
// nsIContent interface
545
//
546
void nsXULElement::UpdateEditableState(bool aNotify) {
547
// Don't call through to Element here because the things
548
// it does don't work for cases when we're an editable control.
549
nsIContent* parent = GetParent();
550
551
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
552
UpdateState(aNotify);
553
}
554
555
class XULInContentErrorReporter : public Runnable {
556
public:
557
explicit XULInContentErrorReporter(Document& aDocument)
558
: mozilla::Runnable("XULInContentErrorReporter"), mDocument(aDocument) {}
559
560
NS_IMETHOD Run() override {
561
mDocument->WarnOnceAbout(Document::eImportXULIntoContent, false);
562
return NS_OK;
563
}
564
565
private:
566
OwningNonNull<Document> mDocument;
567
};
568
569
static bool NeedTooltipSupport(const nsXULElement& aXULElement) {
570
if (aXULElement.NodeInfo()->Equals(nsGkAtoms::treechildren)) {
571
// treechildren always get tooltip support, since cropped tree cells show
572
// their full text in a tooltip.
573
return true;
574
}
575
576
return aXULElement.GetBoolAttr(nsGkAtoms::tooltip) ||
577
aXULElement.GetBoolAttr(nsGkAtoms::tooltiptext);
578
}
579
580
nsresult nsXULElement::BindToTree(BindContext& aContext, nsINode& aParent) {
581
nsresult rv = nsStyledElement::BindToTree(aContext, aParent);
582
NS_ENSURE_SUCCESS(rv, rv);
583
584
if (!IsInComposedDoc()) {
585
return rv;
586
}
587
588
Document& doc = aContext.OwnerDoc();
589
if (!IsInNativeAnonymousSubtree() && !doc.IsLoadedAsInteractiveData() &&
590
!doc.AllowXULXBL() &&
591
!doc.HasWarnedAbout(Document::eImportXULIntoContent)) {
592
nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(doc));
593
}
594
595
#ifdef DEBUG
596
if (!doc.AllowXULXBL() && !doc.IsUnstyledDocument()) {
597
// To save CPU cycles and memory, non-XUL documents only load the user
598
// agent style sheet rules for a minimal set of XUL elements such as
599
// 'scrollbar' that may be created implicitly for their content (those
600
// rules being in minimal-xul.css).
601
//
602
// This assertion makes sure no other XUL element is used in a non-XUL
603
// document.
604
nsAtom* tag = NodeInfo()->NameAtom();
605
MOZ_ASSERT(
606
// scrollbar parts
607
tag == nsGkAtoms::scrollbar || tag == nsGkAtoms::scrollbarbutton ||
608
tag == nsGkAtoms::scrollcorner || tag == nsGkAtoms::slider ||
609
tag == nsGkAtoms::thumb ||
610
// other
611
tag == nsGkAtoms::resizer || tag == nsGkAtoms::label,
612
"Unexpected XUL element in non-XUL doc");
613
}
614
#endif
615
616
// Within Bug 1492063 and its dependencies we started to apply a
617
// CSP to system privileged about pages. Since some about: pages
618
// are implemented in *.xul files we added this workaround to
619
// apply a CSP to them. To do so, we check the introduced custom
620
// attribute 'csp' on the root element.
621
if (doc.GetRootElement() == this) {
622
nsAutoString cspPolicyStr;
623
GetAttr(kNameSpaceID_None, nsGkAtoms::csp, cspPolicyStr);
624
625
#ifdef DEBUG
626
{
627
nsCOMPtr<nsIContentSecurityPolicy> docCSP = doc.GetCsp();
628
uint32_t policyCount = 0;
629
if (docCSP) {
630
docCSP->GetPolicyCount(&policyCount);
631
}
632
MOZ_ASSERT(policyCount == 0, "how come we already have a policy?");
633
}
634
#endif
635
636
CSP_ApplyMetaCSPToDoc(doc, cspPolicyStr);
637
}
638
639
if (NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
640
// Create our XUL key listener and hook it up.
641
XULKeySetGlobalKeyListener::AttachKeyHandler(this);
642
}
643
644
if (NeedTooltipSupport(*this)) {
645
AddTooltipSupport();
646
}
647
648
if (XULBroadcastManager::MayNeedListener(*this)) {
649
if (!doc.HasXULBroadcastManager()) {
650
doc.InitializeXULBroadcastManager();
651
}
652
XULBroadcastManager* broadcastManager = doc.GetXULBroadcastManager();
653
broadcastManager->AddListener(this);
654
}
655
return rv;
656
}
657
658
void nsXULElement::UnbindFromTree(bool aNullParent) {
659
if (NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
660
XULKeySetGlobalKeyListener::DetachKeyHandler(this);
661
}
662
663
if (NeedTooltipSupport(*this)) {
664
RemoveTooltipSupport();
665
}
666
667
Document* doc = GetComposedDoc();
668
if (doc && doc->HasXULBroadcastManager() &&
669
XULBroadcastManager::MayNeedListener(*this)) {
670
RefPtr<XULBroadcastManager> broadcastManager =
671
doc->GetXULBroadcastManager();
672
broadcastManager->RemoveListener(this);
673
}
674
675
// mControllers can own objects that are implemented
676
// in JavaScript (such as some implementations of
677
// nsIControllers. These objects prevent their global
678
// object's script object from being garbage collected,
679
// which means JS continues to hold an owning reference
680
// to the nsGlobalWindow, which owns the document,
681
// which owns this content. That's a cycle, so we break
682
// it here. (It might be better to break this by releasing
683
// mDocument in nsGlobalWindow::SetDocShell, but I'm not
684
// sure whether that would fix all possible cycles through
685
// mControllers.)
686
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
687
if (slots) {
688
slots->mControllers = nullptr;
689
}
690
691
nsStyledElement::UnbindFromTree(aNullParent);
692
}
693
694
void nsXULElement::DoneAddingChildren(bool aHaveNotified) {
695
if (IsXULElement(nsGkAtoms::linkset)) {
696
Document* doc = GetComposedDoc();
697
if (doc) {
698
doc->OnL10nResourceContainerParsed();
699
}
700
}
701
}
702
703
void nsXULElement::UnregisterAccessKey(const nsAString& aOldValue) {
704
// If someone changes the accesskey, unregister the old one
705
//
706
Document* doc = GetComposedDoc();
707
if (doc && !aOldValue.IsEmpty()) {
708
if (PresShell* presShell = doc->GetPresShell()) {
709
presShell->GetPresContext()->EventStateManager()->UnregisterAccessKey(
710
this, aOldValue.First());
711
}
712
}
713
}
714
715
nsresult nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
716
const nsAttrValueOrString* aValue,
717
bool aNotify) {
718
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey &&
719
IsInUncomposedDoc()) {
720
nsAutoString oldValue;
721
if (GetAttr(aNamespaceID, aName, oldValue)) {
722
UnregisterAccessKey(oldValue);
723
}
724
} else if (aNamespaceID == kNameSpaceID_None &&
725
(aName == nsGkAtoms::command || aName == nsGkAtoms::observes) &&
726
IsInUncomposedDoc()) {
727
// XXX sXBL/XBL2 issue! Owner or current document?
728
// XXX Why does this not also remove broadcast listeners if the
729
// "element" attribute was changed on an <observer>?
730
nsAutoString oldValue;
731
GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue);
732
if (oldValue.IsEmpty()) {
733
GetAttr(kNameSpaceID_None, nsGkAtoms::command, oldValue);
734
}
735
736
Document* doc = GetUncomposedDoc();
737
if (!oldValue.IsEmpty() && doc->HasXULBroadcastManager()) {
738
RefPtr<XULBroadcastManager> broadcastManager =
739
doc->GetXULBroadcastManager();
740
broadcastManager->RemoveListener(this);
741
}
742
} else if (aNamespaceID == kNameSpaceID_None && aValue &&
743
mNodeInfo->Equals(nsGkAtoms::window) &&
744
aName == nsGkAtoms::chromemargin) {
745
nsAttrValue attrValue;
746
// Make sure the margin format is valid first
747
if (!attrValue.ParseIntMarginValue(aValue->String())) {
748
return NS_ERROR_INVALID_ARG;
749
}
750
} else if (aNamespaceID == kNameSpaceID_None &&
751
aName == nsGkAtoms::usercontextid) {
752
nsAutoString oldValue;
753
bool hasAttribute =
754
GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, oldValue);
755
if (hasAttribute && (!aValue || !aValue->String().Equals(oldValue))) {
756
MOZ_ASSERT(false, "Changing usercontextid is not allowed.");
757
return NS_ERROR_INVALID_ARG;
758
}
759
}
760
761
return nsStyledElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
762
}
763
764
nsresult nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
765
const nsAttrValue* aValue,
766
const nsAttrValue* aOldValue,
767
nsIPrincipal* aSubjectPrincipal,
768
bool aNotify) {
769
if (aNamespaceID == kNameSpaceID_None) {
770
if (aValue) {
771
AddListenerForAttributeIfNeeded(aName);
772
}
773
774
if (aName == nsGkAtoms::tooltip || aName == nsGkAtoms::tooltiptext) {
775
if (!!aValue != !!aOldValue && IsInComposedDoc() &&
776
!NodeInfo()->Equals(nsGkAtoms::treechildren)) {
777
if (aValue) {
778
AddTooltipSupport();
779
} else {
780
RemoveTooltipSupport();
781
}
782
}
783
}
784
Document* doc = GetComposedDoc();
785
if (doc && doc->HasXULBroadcastManager()) {
786
RefPtr<XULBroadcastManager> broadcastManager =
787
doc->GetXULBroadcastManager();
788
broadcastManager->AttributeChanged(this, aNamespaceID, aName);
789
}
790
if (doc && XULBroadcastManager::MayNeedListener(*this)) {
791
if (!doc->HasXULBroadcastManager()) {
792
doc->InitializeXULBroadcastManager();
793
}
794
XULBroadcastManager* broadcastManager = doc->GetXULBroadcastManager();
795
broadcastManager->AddListener(this);
796
}
797
798
// XXX need to check if they're changing an event handler: if
799
// so, then we need to unhook the old one. Or something.
800
}
801
802
return nsStyledElement::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
803
aSubjectPrincipal, aNotify);
804
}
805
806
void nsXULElement::AddTooltipSupport() {
807
nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
808
if (!listener) {
809
return;
810
}
811
812
listener->AddTooltipSupport(this);
813
}
814
815
void nsXULElement::RemoveTooltipSupport() {
816
nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
817
if (!listener) {
818
return;
819
}
820
821
listener->RemoveTooltipSupport(this);
822
}
823
824
bool nsXULElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
825
const nsAString& aValue,
826
nsIPrincipal* aMaybeScriptedPrincipal,
827
nsAttrValue& aResult) {
828
if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
829
return aResult.ParseIntValue(aValue);
830
}
831
832
// Parse into a nsAttrValue
833
if (!nsStyledElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
834
aMaybeScriptedPrincipal, aResult)) {
835
// Fall back to parsing as atom for short values
836
aResult.ParseStringOrAtom(aValue);
837
}
838
839
return true;
840
}
841
842
void nsXULElement::DestroyContent() {
843
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
844
if (slots) {
845
slots->mControllers = nullptr;
846
}
847
848
nsStyledElement::DestroyContent();
849
}
850
851
#ifdef DEBUG
852
void nsXULElement::List(FILE* out, int32_t aIndent) const {
853
nsCString prefix("XUL");
854
if (HasSlots()) {
855
prefix.Append('*');
856
}
857
prefix.Append(' ');
858
859
nsStyledElement::List(out, aIndent, prefix);
860
}
861
#endif
862
863
bool nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage) {
864
return (IsRootOfNativeAnonymousSubtree() &&
865
IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) &&
866
(aMessage == eMouseClick || aMessage == eMouseDoubleClick ||
867
aMessage == eXULCommand || aMessage == eContextMenu ||
868
aMessage == eDragStart || aMessage == eMouseAuxClick));
869
}
870
871
nsresult nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor,
872
nsAutoString& aCommand) {
873
// XXX sXBL/XBL2 issue! Owner or current document?
874
nsCOMPtr<Document> doc = GetUncomposedDoc();
875
NS_ENSURE_STATE(doc);
876
RefPtr<Element> commandElt = doc->GetElementById(aCommand);
877
if (commandElt) {
878
// Create a new command event to dispatch to the element
879
// pointed to by the command attribute. The new event's
880
// sourceEvent will be the original command event that we're
881
// handling.
882
RefPtr<Event> event = aVisitor.mDOMEvent;
883
uint16_t inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
884
while (event) {
885
NS_ENSURE_STATE(event->GetOriginalTarget() != commandElt);
886
RefPtr<XULCommandEvent> commandEvent = event->AsXULCommandEvent();
887
if (commandEvent) {
888
event = commandEvent->GetSourceEvent();
889
inputSource = commandEvent->InputSource();
890
} else {
891
event = nullptr;
892
}
893
}
894
WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent();
895
nsContentUtils::DispatchXULCommand(
896
commandElt, orig->IsTrusted(), MOZ_KnownLive(aVisitor.mDOMEvent),
897
nullptr, orig->IsControl(), orig->IsAlt(), orig->IsShift(),
898
orig->IsMeta(), inputSource);
899
} else {
900
NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
901
}
902
return NS_OK;
903
}
904
905
void nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
906
aVisitor.mForceContentDispatch = true; // FIXME! Bug 329119
907
if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) {
908
// Don't propagate these events from native anonymous scrollbar.
909
aVisitor.mCanHandle = true;
910
aVisitor.SetParentTarget(nullptr, false);
911
return;
912
}
913
if (aVisitor.mEvent->mMessage == eXULCommand &&
914
aVisitor.mEvent->mClass == eInputEventClass &&
915
aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this) &&
916
!IsXULElement(nsGkAtoms::command)) {
917
// Check that we really have an xul command event. That will be handled
918
// in a special way.
919
// See if we have a command elt. If so, we execute on the command
920
// instead of on our content element.
921
if (aVisitor.mDOMEvent && aVisitor.mDOMEvent->AsXULCommandEvent() &&
922
HasNonEmptyAttr(nsGkAtoms::command)) {
923
// Stop building the event target chain for the original event.
924
// We don't want it to propagate to any DOM nodes.
925
aVisitor.mCanHandle = false;
926
aVisitor.mAutomaticChromeDispatch = false;
927
// Dispatch XUL command in PreHandleEvent to prevent it breaks event
928
// target chain creation
929
aVisitor.mWantsPreHandleEvent = true;
930
aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND;
931
return;
932
}
933
}
934
935
nsStyledElement::GetEventTargetParent(aVisitor);
936
}
937
938
nsresult nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor) {
939
if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) {
940
nsAutoString command;
941
GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
942
MOZ_ASSERT(!command.IsEmpty());
943
return DispatchXULCommand(aVisitor, command);
944
}
945
return nsStyledElement::PreHandleEvent(aVisitor);
946
}
947
948
//----------------------------------------------------------------------
949
// Implementation methods
950
951
nsChangeHint nsXULElement::GetAttributeChangeHint(const nsAtom* aAttribute,
952
int32_t aModType) const {
953
if (aAttribute == nsGkAtoms::value &&
954
(aModType == MutationEvent_Binding::REMOVAL ||
955
aModType == MutationEvent_Binding::ADDITION) &&
956
IsAnyOfXULElements(nsGkAtoms::label, nsGkAtoms::description)) {
957
// Label and description dynamically morph between a normal
958
// block and a cropping single-line XUL text frame. If the
959
// value attribute is being added or removed, then we need to
960
// return a hint of frame change. (See bugzilla bug 95475 for
961
// details.)
962
return nsChangeHint_ReconstructFrame;
963
}
964
965
if (aAttribute == nsGkAtoms::type &&
966
IsAnyOfXULElements(nsGkAtoms::toolbarbutton, nsGkAtoms::button)) {
967
// type=menu switches from a button frame to a menu frame.
968
return nsChangeHint_ReconstructFrame;
969
}
970
971
// if left or top changes we reflow. This will happen in xul
972
// containers that manage positioned children such as a stack.
973
if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute ||
974
nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute ||
975
nsGkAtoms::start == aAttribute || nsGkAtoms::end == aAttribute) {
976
return NS_STYLE_HINT_REFLOW;
977
}
978
979
return nsChangeHint(0);
980
}
981
982
NS_IMETHODIMP_(bool)
983
nsXULElement::IsAttributeMapped(const nsAtom* aAttribute) const {
984
return false;
985
}
986
987
nsIControllers* nsXULElement::GetControllers(ErrorResult& rv) {
988
if (!Controllers()) {
989
nsExtendedDOMSlots* slots = ExtendedDOMSlots();
990
991
slots->mControllers = new nsXULControllers();
992
}
993
994
return Controllers();
995
}
996
997
void nsXULElement::Click(CallerType aCallerType) {
998
ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
999
aCallerType == CallerType::System);
1000
}
1001
1002
void nsXULElement::ClickWithInputSource(uint16_t aInputSource,
1003
bool aIsTrustedEvent) {
1004
if (BoolAttrIsTrue(nsGkAtoms::disabled)) return;
1005
1006
nsCOMPtr<Document> doc = GetComposedDoc(); // Strong just in case
1007
if (doc) {
1008
RefPtr<nsPresContext> context = doc->GetPresContext();
1009
if (context) {
1010
// strong ref to PresContext so events don't destroy it
1011
1012
WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown, nullptr,
1013
WidgetMouseEvent::eReal);
1014
WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp, nullptr,
1015
WidgetMouseEvent::eReal);
1016
WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr,
1017
WidgetMouseEvent::eReal);
1018
eventDown.mInputSource = eventUp.mInputSource = eventClick.mInputSource =
1019
aInputSource;
1020
1021
// send mouse down
1022
nsEventStatus status = nsEventStatus_eIgnore;
1023
EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
1024
&eventDown, nullptr, &status);
1025
1026
// send mouse up
1027
status = nsEventStatus_eIgnore; // reset status
1028
EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
1029
&eventUp, nullptr, &status);
1030
1031
// send mouse click
1032
status = nsEventStatus_eIgnore; // reset status
1033
EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
1034
&eventClick, nullptr, &status);
1035
1036
// If the click has been prevented, lets skip the command call
1037
// this is how a physical click works
1038
if (status == nsEventStatus_eConsumeNoDefault) {
1039
return;
1040
}
1041
}
1042
}
1043
1044
// oncommand is fired when an element is clicked...
1045
DoCommand();
1046
}
1047
1048
void nsXULElement::DoCommand() {
1049
nsCOMPtr<Document> doc = GetComposedDoc(); // strong just in case
1050
if (doc) {
1051
RefPtr<nsXULElement> self = this;
1052
nsContentUtils::DispatchXULCommand(self, true);
1053
}
1054
}
1055
1056
bool nsXULElement::IsNodeOfType(uint32_t aFlags) const { return false; }
1057
1058
nsresult nsXULElement::AddPopupListener(nsAtom* aName) {
1059
// Add a popup listener to the element
1060
bool isContext =
1061
(aName == nsGkAtoms::context || aName == nsGkAtoms::contextmenu);
1062
uint32_t listenerFlag = isContext ? XUL_ELEMENT_HAS_CONTENTMENU_LISTENER
1063
: XUL_ELEMENT_HAS_POPUP_LISTENER;
1064
1065
if (HasFlag(listenerFlag)) {
1066
return NS_OK;
1067
}
1068
1069
nsCOMPtr<nsIDOMEventListener> listener =
1070
new nsXULPopupListener(this, isContext);
1071
1072
// Add the popup as a listener on this element.
1073
EventListenerManager* manager = GetOrCreateListenerManager();
1074
SetFlags(listenerFlag);
1075
1076
if (isContext) {
1077
manager->AddEventListenerByType(listener, NS_LITERAL_STRING("contextmenu"),
1078
TrustedEventsAtSystemGroupBubble());
1079
} else {
1080
manager->AddEventListenerByType(listener, NS_LITERAL_STRING("mousedown"),
1081
TrustedEventsAtSystemGroupBubble());
1082
}
1083
return NS_OK;
1084
}
1085
1086
//----------------------------------------------------------------------
1087
1088
nsresult nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) {
1089
if (!aPrototype) {
1090
return NS_OK;
1091
}
1092
1093
size_t i;
1094
nsresult rv;
1095
for (i = 0; i < aPrototype->mAttributes.Length(); ++i) {
1096
nsXULPrototypeAttribute* protoattr = &aPrototype->mAttributes[i];
1097
nsAttrValue attrValue;
1098
1099
// Style rules need to be cloned.
1100
if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) {
1101
DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue();
1102
RefPtr<DeclarationBlock> declClone = decl->Clone();
1103
1104
nsString stringValue;
1105
protoattr->mValue.ToString(stringValue);
1106
1107
attrValue.SetTo(declClone.forget(), &stringValue);
1108
} else {
1109
attrValue.SetTo(protoattr->mValue);
1110
}
1111
1112
bool oldValueSet;
1113
// XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName
1114
if (protoattr->mName.IsAtom()) {
1115
rv = mAttrs.SetAndSwapAttr(protoattr->mName.Atom(), attrValue,
1116
&oldValueSet);
1117
} else {
1118
rv = mAttrs.SetAndSwapAttr(protoattr->mName.NodeInfo(), attrValue,
1119
&oldValueSet);
1120
}
1121
NS_ENSURE_SUCCESS(rv, rv);
1122
}
1123
return NS_OK;
1124
}
1125
1126
bool nsXULElement::BoolAttrIsTrue(nsAtom* aName) const {
1127
const nsAttrValue* attr = GetAttrInfo(kNameSpaceID_None, aName).mValue;
1128
1129
return attr && attr->Type() == nsAttrValue::eAtom &&
1130
attr->GetAtomValue() == nsGkAtoms::_true;
1131
}
1132
1133
void nsXULElement::RecompileScriptEventListeners() {
1134
int32_t i, count = mAttrs.AttrCount();
1135
for (i = 0; i < count; ++i) {
1136
const nsAttrName* name = mAttrs.AttrNameAt(i);
1137
1138
// Eventlistenener-attributes are always in the null namespace
1139
if (!name->IsAtom()) {
1140
continue;
1141
}
1142
1143
nsAtom* attr = name->Atom();
1144
if (!nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) {
1145
continue;
1146
}
1147
1148
nsAutoString value;
1149
GetAttr(kNameSpaceID_None, attr, value);
1150
SetEventHandler(attr, value, true);
1151
}
1152
}
1153
1154
bool nsXULElement::IsEventAttributeNameInternal(nsAtom* aName) {
1155
return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL);
1156
}
1157
1158
JSObject* nsXULElement::WrapNode(JSContext* aCx,
1159
JS::Handle<JSObject*> aGivenProto) {
1160
return dom::XULElement_Binding::Wrap(aCx, this, aGivenProto);
1161
}
1162
1163
bool nsXULElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const {
1164
return IsXULElement(nsGkAtoms::menupopup) ||
1165
Element::IsInteractiveHTMLContent(aIgnoreTabindex);
1166
}
1167
1168
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
1169
1170
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode)
1171
if (tmp->mType == nsXULPrototypeNode::eType_Element) {
1172
static_cast<nsXULPrototypeElement*>(tmp)->Unlink();
1173
} else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
1174
static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects();
1175
}
1176
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1177
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode)
1178
if (tmp->mType == nsXULPrototypeNode::eType_Element) {
1179
nsXULPrototypeElement* elem = static_cast<nsXULPrototypeElement*>(tmp);
1180
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo");
1181
cb.NoteNativeChild(elem->mNodeInfo,
1182
NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1183
size_t i;
1184
for (i = 0; i < elem->mAttributes.Length(); ++i) {
1185
const nsAttrName& name = elem->mAttributes[i].mName;
1186
if (!name.IsAtom()) {
1187
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1188
"mAttributes[i].mName.NodeInfo()");
1189
cb.NoteNativeChild(name.NodeInfo(),
1190
NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1191
}
1192
}
1193
ImplCycleCollectionTraverse(cb, elem->mChildren, "mChildren");
1194
}
1195
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1196
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode)
1197
if (tmp->mType == nsXULPrototypeNode::eType_Script) {
1198
nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(tmp);
1199
script->Trace(aCallbacks, aClosure);
1200
}
1201
NS_IMPL_CYCLE_COLLECTION_TRACE_END
1202
1203
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
1204
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release)
1205
1206
//----------------------------------------------------------------------
1207
//
1208
// nsXULPrototypeAttribute
1209
//
1210
1211
nsXULPrototypeAttribute::~nsXULPrototypeAttribute() {
1212
MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
1213
}
1214
1215
//----------------------------------------------------------------------
1216
//
1217
// nsXULPrototypeElement
1218
//
1219
1220
nsresult nsXULPrototypeElement::Serialize(
1221
nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1222
const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1223
nsresult rv;
1224
1225
// Write basic prototype data
1226
rv = aStream->Write32(mType);
1227
1228
// Write Node Info
1229
int32_t index = aNodeInfos->IndexOf(mNodeInfo);
1230
NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
1231
nsresult tmp = aStream->Write32(index);
1232
if (NS_FAILED(tmp)) {
1233
rv = tmp;
1234
}
1235
1236
// Write Attributes
1237
tmp = aStream->Write32(mAttributes.Length());
1238
if (NS_FAILED(tmp)) {
1239
rv = tmp;
1240
}
1241
1242
nsAutoString attributeValue;
1243
size_t i;
1244
for (i = 0; i < mAttributes.Length(); ++i) {
1245
RefPtr<mozilla::dom::NodeInfo> ni;
1246
if (mAttributes[i].mName.IsAtom()) {
1247
ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(
1248
mAttributes[i].mName.Atom(), nullptr, kNameSpaceID_None,
1249
nsINode::ATTRIBUTE_NODE);
1250
NS_ASSERTION(ni, "the nodeinfo should already exist");
1251
} else {
1252
ni = mAttributes[i].mName.NodeInfo();
1253
}
1254
1255
index = aNodeInfos->IndexOf(ni);
1256
NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
1257
tmp = aStream->Write32(index);
1258
if (NS_FAILED(tmp)) {
1259
rv = tmp;
1260
}
1261
1262
mAttributes[i].mValue.ToString(attributeValue);
1263
tmp = aStream->WriteWStringZ(attributeValue.get());
1264
if (NS_FAILED(tmp)) {
1265
rv = tmp;
1266
}
1267
}
1268
1269
// Now write children
1270
tmp = aStream->Write32(uint32_t(mChildren.Length()));
1271
if (NS_FAILED(tmp)) {
1272
rv = tmp;
1273
}
1274
for (i = 0; i < mChildren.Length(); i++) {
1275
nsXULPrototypeNode* child = mChildren[i].get();
1276
switch (child->mType) {
1277
case eType_Element:
1278
case eType_Text:
1279
case eType_PI:
1280
tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos);
1281
if (NS_FAILED(tmp)) {
1282
rv = tmp;
1283
}
1284
break;
1285
case eType_Script:
1286
tmp = aStream->Write32(child->mType);
1287
if (NS_FAILED(tmp)) {
1288
rv = tmp;
1289
}
1290
nsXULPrototypeScript* script =
1291
static_cast<nsXULPrototypeScript*>(child);
1292
1293
tmp = aStream->Write8(script->mOutOfLine);
1294
if (NS_FAILED(tmp)) {
1295
rv = tmp;
1296
}
1297
if (!script->mOutOfLine) {
1298
tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos);
1299
if (NS_FAILED(tmp)) {
1300
rv = tmp;
1301
}
1302
} else {
1303
tmp = aStream->WriteCompoundObject(script->mSrcURI,
1304
NS_GET_IID(nsIURI), true);
1305
if (NS_FAILED(tmp)) {
1306
rv = tmp;
1307
}
1308
1309
if (script->HasScriptObject()) {
1310
// This may return NS_OK without muxing script->mSrcURI's
1311
// data into the cache file, in the case where that
1312
// muxed document is already there (written by a prior
1313
// session, or by an earlier cache episode during this
1314
// session).
1315
tmp = script->SerializeOutOfLine(aStream, aProtoDoc);
1316
if (NS_FAILED(tmp)) {
1317
rv = tmp;
1318
}
1319
}
1320
}
1321
break;
1322
}
1323
}
1324
1325
return rv;
1326
}
1327
1328
nsresult nsXULPrototypeElement::Deserialize(
1329
nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1330
nsIURI* aDocumentURI,
1331
const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1332
MOZ_ASSERT(aNodeInfos, "missing nodeinfo array");
1333
1334
// Read Node Info
1335
uint32_t number = 0;
1336
nsresult rv = aStream->Read32(&number);
1337
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1338
mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr);
1339
if (!mNodeInfo) {
1340
return NS_ERROR_UNEXPECTED;
1341
}
1342
1343
// Read Attributes
1344
rv = aStream->Read32(&number);
1345
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1346
int32_t attributes = int32_t(number);
1347
1348
if (attributes > 0) {
1349
mAttributes.AppendElements(attributes);
1350
1351
nsAutoString attributeValue;
1352
for (size_t i = 0; i < mAttributes.Length(); ++i) {
1353
rv = aStream->Read32(&number);
1354
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1355
mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr);
1356
if (!ni) {
1357
return NS_ERROR_UNEXPECTED;
1358
}
1359
1360
mAttributes[i].mName.SetTo(ni);
1361
1362
rv = aStream->ReadString(attributeValue);
1363
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1364
rv = SetAttrAt(i, attributeValue, aDocumentURI);
1365
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1366
}
1367
}
1368
1369
rv = aStream->Read32(&number);
1370
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1371
uint32_t numChildren = int32_t(number);
1372
1373
if (numChildren > 0) {
1374
if (!mChildren.SetCapacity(numChildren, fallible)) {
1375
return NS_ERROR_OUT_OF_MEMORY;
1376
}
1377
1378
for (uint32_t i = 0; i < numChildren; i++) {
1379
rv = aStream->Read32(&number);
1380
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1381
Type childType = (Type)number;
1382
1383
RefPtr<nsXULPrototypeNode> child;
1384
1385
switch (childType) {
1386
case eType_Element:
1387
child = new nsXULPrototypeElement();
1388
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, aNodeInfos);
1389
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1390
break;
1391
case eType_Text:
1392
child = new nsXULPrototypeText();
1393
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, aNodeInfos);
1394
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1395
break;
1396
case eType_PI:
1397
child = new nsXULPrototypePI();
1398
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, aNodeInfos);
1399
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1400
break;
1401
case eType_Script: {
1402
// language version/options obtained during deserialization.
1403
RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0);
1404
1405
rv = aStream->ReadBoolean(&script->mOutOfLine);
1406
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1407
if (!script->mOutOfLine) {
1408
rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
1409
aNodeInfos);
1410
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1411
} else {
1412
nsCOMPtr<nsISupports> supports;
1413
rv = aStream->ReadObject(true, getter_AddRefs(supports));
1414
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1415
script->mSrcURI = do_QueryInterface(supports);
1416
1417
rv = script->DeserializeOutOfLine(aStream, aProtoDoc);
1418
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1419
}
1420
1421
child = script.forget();
1422
break;
1423
}
1424
default:
1425
MOZ_ASSERT(false, "Unexpected child type!");
1426
return NS_ERROR_UNEXPECTED;
1427
}
1428
1429
MOZ_ASSERT(child, "Don't append null to mChildren");
1430
MOZ_ASSERT(child->mType == childType);
1431
mChildren.AppendElement(child);
1432
1433
// Oh dear. Something failed during the deserialization.
1434
// We don't know what. But likely consequences of failed
1435
// deserializations included calls to |AbortCaching| which
1436
// shuts down the cache and closes our streams.
1437
// If that happens, next time through this loop, we die a messy
1438
// death. So, let's just fail now, and propagate that failure
1439
// upward so that the ChromeProtocolHandler knows it can't use
1440
// a cached chrome channel for this.
1441
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1442
}
1443
}
1444
1445
return rv;
1446
}
1447
1448
nsresult nsXULPrototypeElement::SetAttrAt(uint32_t aPos,
1449
const nsAString& aValue,
1450
nsIURI* aDocumentURI) {
1451
MOZ_ASSERT(aPos < mAttributes.Length(), "out-of-bounds");
1452
1453
// WARNING!!
1454
// This code is largely duplicated in nsXULElement::SetAttr.
1455
// Any changes should be made to both functions.
1456
1457
if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
1458
if (mNodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
1459
mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
1460
// We still care about the is attribute set on HTML elements.
1461
mAttributes[aPos].mValue.ParseAtom(aValue);
1462
mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
1463
1464
return NS_OK;
1465
}
1466
1467
mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
1468
1469
return NS_OK;
1470
}
1471
1472
if (mAttributes[aPos].mName.Equals(nsGkAtoms::id) && !aValue.IsEmpty()) {
1473
mHasIdAttribute = true;
1474
// Store id as atom.
1475
// id="" means that the element has no id. Not that it has
1476
// emptystring as id.
1477
mAttributes[aPos].mValue.ParseAtom(aValue);
1478
1479
return NS_OK;
1480
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
1481
// Store is as atom.
1482
mAttributes[aPos].mValue.ParseAtom(aValue);
1483
mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
1484
1485
return NS_OK;
1486
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) {
1487
mHasClassAttribute = true;
1488
// Compute the element's class list
1489
mAttributes[aPos].mValue.ParseAtomArray(aValue);
1490
1491
return NS_OK;
1492
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) {
1493
mHasStyleAttribute = true;
1494
// Parse the element's 'style' attribute
1495
1496
// This is basically duplicating what nsINode::NodePrincipal() does
1497
nsIPrincipal* principal = mNodeInfo->NodeInfoManager()->DocumentPrincipal();
1498
// XXX Get correct Base URI (need GetBaseURI on *prototype* element)
1499
// TODO: If we implement Content Security Policy for chrome documents
1500
// as has been discussed, the CSP should be checked here to see if
1501
// inline styles are allowed to be applied.
1502
// XXX No specific specs talk about xul and referrer policy, pass Unset
1503
nsCOMPtr<nsIReferrerInfo> referrerInfo =
1504
new ReferrerInfo(aDocumentURI, ReferrerPolicy::_empty);
1505
1506
RefPtr<URLExtraData> data =
1507
new URLExtraData(aDocumentURI, referrerInfo, principal);
1508
RefPtr<DeclarationBlock> declaration = DeclarationBlock::FromCssText(
1509
aValue, data, eCompatibility_FullStandards, nullptr);
1510
if (declaration) {
1511
mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue);
1512
1513
return NS_OK;
1514
}
1515
// Don't abort if parsing failed, it could just be malformed css.
1516
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::tabindex)) {
1517
mAttributes[aPos].mValue.ParseIntValue(aValue);
1518
1519
return NS_OK;
1520
}
1521
1522
mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
1523
1524
return NS_OK;
1525
}
1526
1527
void nsXULPrototypeElement::Unlink() {
1528
mAttributes.Clear();
1529
mChildren.Clear();
1530
}
1531
1532
void nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc) {
1533
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
1534
nsXULPrototypeNode* child = mChildren[i];
1535
if (child->mType == nsXULPrototypeNode::eType_Element) {
1536
static_cast<nsXULPrototypeElement*>(child)->TraceAllScripts(aTrc);
1537
} else if (child->mType == nsXULPrototypeNode::eType_Script) {
1538
static_cast<nsXULPrototypeScript*>(child)->TraceScriptObject(aTrc);
1539
}
1540
}
1541
}
1542
1543
//----------------------------------------------------------------------
1544
//
1545
// nsXULPrototypeScript
1546
//
1547
1548
nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo)
1549
: nsXULPrototypeNode(eType_Script),
1550
mLineNo(aLineNo),
1551
mSrcLoading(false),
1552
mOutOfLine(true),
1553
mSrcLoadWaiters(nullptr),
1554
mScriptObject(nullptr) {}
1555
1556
nsXULPrototypeScript::~nsXULPrototypeScript() { UnlinkJSObjects(); }
1557
1558
nsresult nsXULPrototypeScript::Serialize(
1559
nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1560
const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1561
NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED);
1562
1563
AutoJSAPI jsapi;
1564
if (!jsapi.Init(xpc::CompilationScope())) {
1565
return NS_ERROR_UNEXPECTED;
1566
}
1567
1568
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mScriptObject,
1569
"script source still loading when serializing?!");
1570
if (!mScriptObject) return NS_ERROR_FAILURE;
1571
1572
// Write basic prototype data
1573
nsresult rv;
1574
rv = aStream->Write32(mLineNo);
1575
if (NS_FAILED(rv)) return rv;
1576
rv = aStream->Write32(0); // See bug 1418294.
1577
if (NS_FAILED(rv)) return rv;
1578
1579
JSContext* cx = jsapi.cx();
1580
JS::Rooted<JSScript*> script(cx, mScriptObject);
1581
MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
1582
return nsContentUtils::XPConnect()->WriteScript(aStream, cx, script);
1583
}
1584
1585
nsresult nsXULPrototypeScript::SerializeOutOfLine(
1586
nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc) {
1587
if (!mSrcURI->SchemeIs("chrome"))
1588
// Don't cache scripts that don't come from chrome uris.
1589
return NS_ERROR_NOT_IMPLEMENTED;
1590
1591
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1592
if (!cache) return NS_ERROR_OUT_OF_MEMORY;
1593
1594
NS_ASSERTION(cache->IsEnabled(),
1595
"writing to the cache file, but the XUL cache is off?");
1596
bool exists;
1597
cache->HasData(mSrcURI, &exists);
1598
1599
/* return will be NS_OK from GetAsciiSpec.
1600
* that makes no sense.
1601
* nor does returning NS_OK from HasMuxedDocument.
1602
* XXX return something meaningful.
1603
*/
1604
if (exists) return NS_OK;
1605
1606
nsCOMPtr<nsIObjectOutputStream> oos;
1607
nsresult rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
1608
NS_ENSURE_SUCCESS(rv, rv);
1609
1610
nsresult tmp = Serialize(oos, aProtoDoc, nullptr);
1611
if (NS_FAILED(tmp)) {
1612
rv = tmp;
1613
}
1614
tmp = cache->FinishOutputStream(mSrcURI);
1615
if (NS_FAILED(tmp)) {
1616
rv = tmp;
1617
}
1618
1619
if (NS_FAILED(rv)) cache->AbortCaching();
1620
return rv;
1621
}
1622
1623
nsresult nsXULPrototypeScript::Deserialize(
1624
nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
1625
nsIURI* aDocumentURI,
1626
const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
1627
nsresult rv;
1628
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mScriptObject,
1629
"prototype script not well-initialized when deserializing?!");
1630
1631
// Read basic prototype data
1632
rv = aStream->Read32(&mLineNo);
1633
if (NS_FAILED(rv)) return rv;
1634
uint32_t dummy;
1635
rv = aStream->Read32(&dummy); // See bug 1418294.
1636
if (NS_FAILED(rv)) return rv;
1637
1638
AutoJSAPI jsapi;
1639
if (!jsapi.Init(xpc::CompilationScope())) {
1640
return NS_ERROR_UNEXPECTED;
1641
}
1642
JSContext* cx = jsapi.cx();
1643
1644
JS::Rooted<JSScript*> newScriptObject(cx);
1645
rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
1646
newScriptObject.address());
1647
NS_ENSURE_SUCCESS(rv, rv);
1648
Set(newScriptObject);
1649
return NS_OK;
1650
}
1651
1652
nsresult nsXULPrototypeScript::DeserializeOutOfLine(
1653
nsIObjectInputStream* aInput, nsXULPrototypeDocument* aProtoDoc) {
1654
// Keep track of failure via rv, so we can
1655
// AbortCaching if things look bad.
1656
nsresult rv = NS_OK;
1657
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1658
1659
nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
1660
if (cache) {
1661
bool useXULCache = true;
1662
if (mSrcURI) {
1663
// NB: we must check the XUL script cache early, to avoid
1664
// multiple deserialization attempts for a given script.
1665
// Note that PrototypeDocumentContentSink::LoadScript
1666
// checks the XUL script cache too, in order to handle the
1667
// serialization case.
1668
//
1669
// We need do this only for <script src='strres.js'> and the
1670
// like, i.e., out-of-line scripts that are included by several
1671
// different XUL documents stored in the cache file.
1672
useXULCache = cache->IsEnabled();
1673
1674
if (useXULCache) {
1675
JSScript* newScriptObject = cache->GetScript(mSrcURI);
1676
if (newScriptObject) Set(newScriptObject);
1677
}
1678
}
1679
1680
if (!mScriptObject) {
1681
if (mSrcURI) {
1682
rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput));
1683
}
1684
// If !mSrcURI, we have an inline script. We shouldn't have
1685
// to do anything else in that case, I think.
1686
1687
// We do reflect errors into rv, but our caller may want to
1688
// ignore our return value, because mScriptObject will be null
1689
// after any error, and that suffices to cause the script to
1690
// be reloaded (from the src= URI, if any) and recompiled.
1691
// We're better off slow-loading than bailing out due to a
1692
// error.
1693
if (NS_SUCCEEDED(rv))
1694
rv =