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