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