Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xul/nsXULElement.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "nsIDocument.h"
16
#include "mozilla/ClearOnShutdown.h"
17
#include "mozilla/EventListenerManager.h"
18
#include "mozilla/EventStateManager.h"
19
#include "mozilla/EventStates.h"
20
#include "mozilla/DeclarationBlock.h"
21
#include "js/CompilationAndEvaluation.h"
22
#include "js/SourceBufferHolder.h"
23
#include "nsFocusManager.h"
24
#include "nsHTMLStyleSheet.h"
25
#include "nsNameSpaceManager.h"
26
#include "nsIObjectInputStream.h"
27
#include "nsIObjectOutputStream.h"
28
#include "nsIPresShell.h"
29
#include "nsIPrincipal.h"
30
#include "nsIScriptContext.h"
31
#include "nsIScriptError.h"
32
#include "nsIScriptSecurityManager.h"
33
#include "nsIServiceManager.h"
34
#include "nsIURL.h"
35
#include "nsViewManager.h"
36
#include "nsIWidget.h"
37
#include "nsLayoutCID.h"
38
#include "nsContentCID.h"
39
#include "mozilla/dom/Event.h"
40
#include "nsStyleConsts.h"
41
#include "nsString.h"
42
#include "nsXULControllers.h"
43
#include "nsIBoxObject.h"
44
#include "nsPIBoxObject.h"
45
#include "XULDocument.h"
46
#include "nsXULPopupListener.h"
47
#include "nsContentUtils.h"
48
#include "nsContentList.h"
49
#include "mozilla/InternalMutationEvent.h"
50
#include "mozilla/MouseEvents.h"
51
#include "nsPIDOMWindow.h"
52
#include "nsJSPrincipals.h"
53
#include "nsDOMAttributeMap.h"
54
#include "nsGkAtoms.h"
55
#include "nsNodeUtils.h"
56
#include "nsFrameLoader.h"
57
#include "mozilla/Logging.h"
58
#include "nsIControllers.h"
59
#include "nsAttrValueOrString.h"
60
#include "nsAttrValueInlines.h"
61
#include "mozilla/Attributes.h"
62
#include "nsIController.h"
63
#include "nsQueryObject.h"
64
#include <algorithm>
65
#include "nsIDOMChromeWindow.h"
66
67
#include "nsReadableUtils.h"
68
#include "nsIFrame.h"
69
#include "nsNodeInfoManager.h"
70
#include "nsXBLBinding.h"
71
#include "nsXULTooltipListener.h"
72
#include "mozilla/EventDispatcher.h"
73
#include "mozAutoDocUpdate.h"
74
#include "nsCCUncollectableMarker.h"
75
#include "nsICSSDeclaration.h"
76
#include "nsLayoutUtils.h"
77
#include "XULFrameElement.h"
78
#include "XULMenuElement.h"
79
#include "XULPopupElement.h"
80
#include "XULScrollElement.h"
81
82
#include "mozilla/dom/XULElementBinding.h"
83
#include "mozilla/dom/BoxObject.h"
84
#include "mozilla/dom/MouseEventBinding.h"
85
#include "mozilla/dom/MutationEventBinding.h"
86
#include "mozilla/dom/XULCommandEvent.h"
87
88
using namespace mozilla;
89
using namespace mozilla::dom;
90
91
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
92
uint32_t             nsXULPrototypeAttribute::gNumElements;
93
uint32_t             nsXULPrototypeAttribute::gNumAttributes;
94
uint32_t             nsXULPrototypeAttribute::gNumCacheTests;
95
uint32_t             nsXULPrototypeAttribute::gNumCacheHits;
96
uint32_t             nsXULPrototypeAttribute::gNumCacheSets;
97
uint32_t             nsXULPrototypeAttribute::gNumCacheFills;
98
#endif
99
100
0
#define NS_DISPATCH_XUL_COMMAND     (1 << 0)
101
102
//----------------------------------------------------------------------
103
// nsXULElement
104
//
105
106
nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
107
    : nsStyledElement(std::move(aNodeInfo)),
108
      mBindingParent(nullptr)
109
0
{
110
0
    XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
111
0
112
0
    // We may be READWRITE by default; check.
113
0
    if (IsReadWriteTextElement()) {
114
0
        AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
115
0
        RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
116
0
    }
117
0
}
118
119
nsXULElement::~nsXULElement()
120
0
{
121
0
}
122
123
void
124
nsXULElement::MaybeUpdatePrivateLifetime()
125
0
{
126
0
    if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype,
127
0
                    NS_LITERAL_STRING("navigator:browser"),
128
0
                    eCaseMatters)) {
129
0
        return;
130
0
    }
131
0
132
0
    nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
133
0
    nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr;
134
0
    if (docShell) {
135
0
        docShell->SetAffectPrivateSessionLifetime(false);
136
0
    }
137
0
}
138
139
/* static */
140
nsXULElement* NS_NewBasicXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
141
0
{
142
0
    return new nsXULElement(std::move(aNodeInfo));
143
0
}
144
145
 /* static */
146
nsXULElement* nsXULElement::Construct(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
147
0
{
148
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
149
0
  if (nodeInfo->Equals(nsGkAtoms::menupopup) ||
150
0
      nodeInfo->Equals(nsGkAtoms::popup) ||
151
0
      nodeInfo->Equals(nsGkAtoms::panel) ||
152
0
      nodeInfo->Equals(nsGkAtoms::tooltip)) {
153
0
    return NS_NewXULPopupElement(nodeInfo.forget());
154
0
  }
155
0
156
0
  if (nodeInfo->Equals(nsGkAtoms::iframe) ||
157
0
      nodeInfo->Equals(nsGkAtoms::browser) ||
158
0
      nodeInfo->Equals(nsGkAtoms::editor)) {
159
0
    return new XULFrameElement(nodeInfo.forget());
160
0
  }
161
0
162
0
  if (nodeInfo->Equals(nsGkAtoms::menu) ||
163
0
      nodeInfo->Equals(nsGkAtoms::menulist)) {
164
0
    return new XULMenuElement(nodeInfo.forget());
165
0
  }
166
0
167
0
  if (nodeInfo->Equals(nsGkAtoms::scrollbox)) {
168
0
    return new XULScrollElement(nodeInfo.forget());
169
0
  }
170
0
171
0
  return NS_NewBasicXULElement(nodeInfo.forget());
172
0
}
173
174
/* static */
175
already_AddRefed<nsXULElement>
176
nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
177
                                  mozilla::dom::NodeInfo *aNodeInfo,
178
                                  bool aIsScriptable,
179
                                  bool aIsRoot)
180
0
{
181
0
    RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
182
0
    nsCOMPtr<Element> baseElement;
183
0
    NS_NewXULElement(getter_AddRefs(baseElement),
184
0
                     ni.forget(),
185
0
                     dom::FROM_PARSER_NETWORK,
186
0
                     aPrototype->mIsAtom);
187
0
188
0
    if (baseElement) {
189
0
        nsXULElement* element = FromNode(baseElement);
190
0
191
0
        if (aPrototype->mHasIdAttribute) {
192
0
            element->SetHasID();
193
0
        }
194
0
        if (aPrototype->mHasClassAttribute) {
195
0
            element->SetMayHaveClass();
196
0
        }
197
0
        if (aPrototype->mHasStyleAttribute) {
198
0
            element->SetMayHaveStyle();
199
0
        }
200
0
201
0
        element->MakeHeavyweight(aPrototype);
202
0
        if (aIsScriptable) {
203
0
            // Check each attribute on the prototype to see if we need to do
204
0
            // any additional processing and hookup that would otherwise be
205
0
            // done 'automagically' by SetAttr().
206
0
            for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
207
0
                element->AddListenerFor(aPrototype->mAttributes[i].mName);
208
0
            }
209
0
        }
210
0
211
0
        if (aIsRoot && aPrototype->mNodeInfo->Equals(nsGkAtoms::window)) {
212
0
            for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
213
0
                if (aPrototype->mAttributes[i].mName.Equals(nsGkAtoms::windowtype)) {
214
0
                    element->MaybeUpdatePrivateLifetime();
215
0
                }
216
0
            }
217
0
        }
218
0
219
0
        return baseElement.forget().downcast<nsXULElement>();
220
0
    }
221
0
222
0
    return nullptr;
223
0
}
224
225
nsresult
226
nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
227
                                  nsIDocument* aDocument,
228
                                  bool aIsScriptable,
229
                                  bool aIsRoot,
230
                                  Element** aResult)
231
0
{
232
0
    // Create an nsXULElement from a prototype
233
0
    MOZ_ASSERT(aPrototype != nullptr, "null ptr");
234
0
    if (! aPrototype)
235
0
        return NS_ERROR_NULL_POINTER;
236
0
237
0
    MOZ_ASSERT(aResult != nullptr, "null ptr");
238
0
    if (! aResult)
239
0
        return NS_ERROR_NULL_POINTER;
240
0
241
0
    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
242
0
    if (aDocument) {
243
0
        mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo;
244
0
        nodeInfo = aDocument->NodeInfoManager()->
245
0
          GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(),
246
0
                      ELEMENT_NODE);
247
0
    } else {
248
0
        nodeInfo = aPrototype->mNodeInfo;
249
0
    }
250
0
251
0
    RefPtr<nsXULElement> element = CreateFromPrototype(aPrototype, nodeInfo,
252
0
                                                       aIsScriptable, aIsRoot);
253
0
    element.forget(aResult);
254
0
255
0
    return NS_OK;
256
0
}
257
258
nsresult
259
NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
260
                 FromParser aFromParser, nsAtom* aIsAtom,
261
                 mozilla::dom::CustomElementDefinition* aDefinition)
262
0
{
263
0
    RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
264
0
265
0
    MOZ_ASSERT(nodeInfo, "need nodeinfo for non-proto Create");
266
0
267
0
    NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
268
0
                 "Trying to create XUL elements that don't have the XUL namespace");
269
0
270
0
    nsIDocument* doc = nodeInfo->GetDocument();
271
0
    if (doc && !doc->AllowXULXBL()) {
272
0
        return NS_ERROR_NOT_AVAILABLE;
273
0
    }
274
0
275
0
    return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIsAtom, aDefinition);
276
0
}
277
278
void
279
NS_TrustedNewXULElement(Element** aResult,
280
                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
281
0
{
282
0
    RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
283
0
    MOZ_ASSERT(ni, "need nodeinfo for non-proto Create");
284
0
285
0
    // Create an nsXULElement with the specified namespace and tag.
286
0
    NS_ADDREF(*aResult = nsXULElement::Construct(ni.forget()));
287
0
}
288
289
//----------------------------------------------------------------------
290
// nsISupports interface
291
292
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement)
293
294
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement,
295
0
                                                  nsStyledElement)
296
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingParent);
297
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
298
299
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULElement,
300
0
                                                nsStyledElement)
301
0
    // Why aren't we unlinking the prototype?
302
0
    tmp->ClearHasID();
303
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mBindingParent);
304
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
305
306
NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement)
307
NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement)
308
309
0
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement)
310
0
    NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
311
0
312
0
    nsCOMPtr<nsISupports> iface =
313
0
      CustomElementRegistry::CallGetCustomInterface(this, aIID);
314
0
    if (iface) {
315
0
      iface->QueryInterface(aIID, aInstancePtr);
316
0
      if (*aInstancePtr) {
317
0
        return NS_OK;
318
0
      }
319
0
    }
320
0
321
0
NS_INTERFACE_MAP_END_INHERITING(Element)
322
0
323
0
//----------------------------------------------------------------------
324
0
// nsINode interface
325
0
326
0
nsresult
327
0
nsXULElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const
328
0
{
329
0
    *aResult = nullptr;
330
0
331
0
    RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
332
0
    RefPtr<nsXULElement> element = Construct(ni.forget());
333
0
334
0
    nsresult rv = element->mAttrs.EnsureCapacityToClone(mAttrs);
335
0
    NS_ENSURE_SUCCESS(rv, rv);
336
0
337
0
    // Note that we're _not_ copying mControllers.
338
0
339
0
    uint32_t count = mAttrs.AttrCount();
340
0
    rv = NS_OK;
341
0
    for (uint32_t i = 0; i < count; ++i) {
342
0
        const nsAttrName* originalName = mAttrs.AttrNameAt(i);
343
0
        const nsAttrValue* originalValue = mAttrs.AttrAt(i);
344
0
        nsAttrValue attrValue;
345
0
346
0
        // Style rules need to be cloned.
347
0
        if (originalValue->Type() == nsAttrValue::eCSSDeclaration) {
348
0
            DeclarationBlock* decl = originalValue->GetCSSDeclarationValue();
349
0
            RefPtr<DeclarationBlock> declClone = decl->Clone();
350
0
351
0
            nsString stringValue;
352
0
            originalValue->ToString(stringValue);
353
0
354
0
            attrValue.SetTo(declClone.forget(), &stringValue);
355
0
        } else {
356
0
            attrValue.SetTo(*originalValue);
357
0
        }
358
0
359
0
        bool oldValueSet;
360
0
        if (originalName->IsAtom()) {
361
0
           rv = element->mAttrs.SetAndSwapAttr(originalName->Atom(),
362
0
                                               attrValue,
363
0
                                               &oldValueSet);
364
0
        } else {
365
0
            rv = element->mAttrs.SetAndSwapAttr(originalName->NodeInfo(),
366
0
                                                attrValue,
367
0
                                                &oldValueSet);
368
0
        }
369
0
        NS_ENSURE_SUCCESS(rv, rv);
370
0
        element->AddListenerFor(*originalName);
371
0
        if (originalName->Equals(nsGkAtoms::id) &&
372
0
            !originalValue->IsEmptyString()) {
373
0
            element->SetHasID();
374
0
        }
375
0
        if (originalName->Equals(nsGkAtoms::_class)) {
376
0
            element->SetMayHaveClass();
377
0
        }
378
0
        if (originalName->Equals(nsGkAtoms::style)) {
379
0
            element->SetMayHaveStyle();
380
0
        }
381
0
    }
382
0
383
0
    element.forget(aResult);
384
0
    return rv;
385
0
}
386
387
//----------------------------------------------------------------------
388
389
EventListenerManager*
390
nsXULElement::GetEventListenerManagerForAttr(nsAtom* aAttrName, bool* aDefer)
391
0
{
392
0
    // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc()
393
0
    // here, override BindToTree for those classes and munge event
394
0
    // listeners there?
395
0
    nsIDocument* doc = OwnerDoc();
396
0
397
0
    nsPIDOMWindowInner *window;
398
0
    Element *root = doc->GetRootElement();
399
0
    if ((!root || root == this) && (window = doc->GetInnerWindow())) {
400
0
401
0
        nsCOMPtr<EventTarget> piTarget = do_QueryInterface(window);
402
0
403
0
        *aDefer = false;
404
0
        return piTarget->GetOrCreateListenerManager();
405
0
    }
406
0
407
0
    return nsStyledElement::GetEventListenerManagerForAttr(aAttrName, aDefer);
408
0
}
409
410
// returns true if the element is not a list
411
static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo)
412
0
{
413
0
  return !aNodeInfo->Equals(nsGkAtoms::tree) &&
414
0
         !aNodeInfo->Equals(nsGkAtoms::richlistbox);
415
0
}
416
417
bool
418
nsXULElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse)
419
0
{
420
0
  /*
421
0
   * Returns true if an element may be focused, and false otherwise. The inout
422
0
   * argument aTabIndex will be set to the tab order index to be used; -1 for
423
0
   * elements that should not be part of the tab order and a greater value to
424
0
   * indicate its tab order.
425
0
   *
426
0
   * Confusingly, the supplied value for the aTabIndex argument may indicate
427
0
   * whether the element may be focused as a result of the -moz-user-focus
428
0
   * property, where -1 means no and 0 means yes.
429
0
   *
430
0
   * For controls, the element cannot be focused and is not part of the tab
431
0
   * order if it is disabled.
432
0
   *
433
0
   * Controls (those that implement nsIDOMXULControlElement):
434
0
   *  *aTabIndex = -1  no tabindex     Not focusable or tabbable
435
0
   *  *aTabIndex = -1  tabindex="-1"   Not focusable or tabbable
436
0
   *  *aTabIndex = -1  tabindex=">=0"  Focusable and tabbable
437
0
   *  *aTabIndex >= 0  no tabindex     Focusable and tabbable
438
0
   *  *aTabIndex >= 0  tabindex="-1"   Focusable but not tabbable
439
0
   *  *aTabIndex >= 0  tabindex=">=0"  Focusable and tabbable
440
0
   * Non-controls:
441
0
   *  *aTabIndex = -1                  Not focusable or tabbable
442
0
   *  *aTabIndex >= 0                  Focusable and tabbable
443
0
   *
444
0
   * If aTabIndex is null, then the tabindex is not computed, and
445
0
   * true is returned for non-disabled controls and false otherwise.
446
0
   */
447
0
448
0
  // elements are not focusable by default
449
0
  bool shouldFocus = false;
450
0
451
#ifdef XP_MACOSX
452
  // on Mac, mouse interactions only focus the element if it's a list,
453
  // or if it's a remote target, since the remote target must handle
454
  // the focus.
455
  if (aWithMouse &&
456
      IsNonList(mNodeInfo) &&
457
      !EventStateManager::IsRemoteTarget(this))
458
  {
459
    return false;
460
  }
461
#endif
462
463
0
  nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this);
464
0
  if (xulControl) {
465
0
    // a disabled element cannot be focused and is not part of the tab order
466
0
    bool disabled;
467
0
    xulControl->GetDisabled(&disabled);
468
0
    if (disabled) {
469
0
      if (aTabIndex)
470
0
        *aTabIndex = -1;
471
0
      return false;
472
0
    }
473
0
    shouldFocus = true;
474
0
  }
475
0
476
0
  if (aTabIndex) {
477
0
    if (xulControl) {
478
0
      if (HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
479
0
        // if either the aTabIndex argument or a specified tabindex is non-negative,
480
0
        // the element becomes focusable.
481
0
        int32_t tabIndex = 0;
482
0
        xulControl->GetTabIndex(&tabIndex);
483
0
        shouldFocus = *aTabIndex >= 0 || tabIndex >= 0;
484
0
        *aTabIndex = tabIndex;
485
0
      } else {
486
0
        // otherwise, if there is no tabindex attribute, just use the value of
487
0
        // *aTabIndex to indicate focusability. Reset any supplied tabindex to 0.
488
0
        shouldFocus = *aTabIndex >= 0;
489
0
        if (shouldFocus)
490
0
          *aTabIndex = 0;
491
0
      }
492
0
493
0
      if (shouldFocus && sTabFocusModelAppliesToXUL &&
494
0
          !(sTabFocusModel & eTabFocus_formElementsMask)) {
495
0
        // By default, the tab focus model doesn't apply to xul element on any system but OS X.
496
0
        // on OS X we're following it for UI elements (XUL) as sTabFocusModel is based on
497
0
        // "Full Keyboard Access" system setting (see mac/nsILookAndFeel).
498
0
        // both textboxes and list elements (i.e. trees and list) should always be focusable
499
0
        // (textboxes are handled as html:input)
500
0
        // For compatibility, we only do this for controls, otherwise elements like <browser>
501
0
        // cannot take this focus.
502
0
        if (IsNonList(mNodeInfo))
503
0
          *aTabIndex = -1;
504
0
      }
505
0
    } else {
506
0
      shouldFocus = *aTabIndex >= 0;
507
0
    }
508
0
  }
509
0
510
0
  return shouldFocus;
511
0
}
512
513
bool
514
nsXULElement::HasMenu()
515
0
{
516
0
  nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
517
0
  return menu != nullptr;
518
0
}
519
520
void
521
nsXULElement::OpenMenu(bool aOpenFlag)
522
0
{
523
0
  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
524
0
  if (doc) {
525
0
    doc->FlushPendingNotifications(FlushType::Frames);
526
0
  }
527
0
528
0
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
529
0
  if (pm) {
530
0
    if (aOpenFlag) {
531
0
      // Nothing will happen if this element isn't a menu.
532
0
      pm->ShowMenu(this, false, false);
533
0
    }
534
0
    else {
535
0
      nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame());
536
0
      if (menu) {
537
0
        nsMenuPopupFrame* popupFrame = menu->GetPopup();
538
0
        if (popupFrame) {
539
0
          pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
540
0
        }
541
0
      }
542
0
    }
543
0
  }
544
0
}
545
546
bool
547
nsXULElement::PerformAccesskey(bool aKeyCausesActivation,
548
                               bool aIsTrustedEvent)
549
0
{
550
0
    RefPtr<Element> content(this);
551
0
552
0
    if (IsXULElement(nsGkAtoms::label)) {
553
0
        nsAutoString control;
554
0
        GetAttr(kNameSpaceID_None, nsGkAtoms::control, control);
555
0
        if (control.IsEmpty()) {
556
0
            return false;
557
0
        }
558
0
559
0
        //XXXsmaug Should we use ShadowRoot::GetElementById in case
560
0
        //         content is in Shadow DOM?
561
0
        nsCOMPtr<nsIDocument> document = content->GetUncomposedDoc();
562
0
        if (!document) {
563
0
            return false;
564
0
        }
565
0
566
0
        content = document->GetElementById(control);
567
0
        if (!content) {
568
0
            return false;
569
0
        }
570
0
    }
571
0
572
0
    nsIFrame* frame = content->GetPrimaryFrame();
573
0
    if (!frame || !frame->IsVisibleConsideringAncestors()) {
574
0
        return false;
575
0
    }
576
0
577
0
    bool focused = false;
578
0
    nsXULElement* elm = FromNode(content);
579
0
    if (elm) {
580
0
        // Define behavior for each type of XUL element.
581
0
        if (!content->IsXULElement(nsGkAtoms::toolbarbutton)) {
582
0
          nsIFocusManager* fm = nsFocusManager::GetFocusManager();
583
0
          if (fm) {
584
0
            nsCOMPtr<Element> elementToFocus;
585
0
            // for radio buttons, focus the radiogroup instead
586
0
            if (content->IsXULElement(nsGkAtoms::radio)) {
587
0
              nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(do_QueryInterface(content));
588
0
              if (controlItem) {
589
0
                bool disabled;
590
0
                controlItem->GetDisabled(&disabled);
591
0
                if (!disabled) {
592
0
                  nsCOMPtr<nsIDOMXULSelectControlElement> selectControl;
593
0
                  controlItem->GetControl(getter_AddRefs(selectControl));
594
0
                  elementToFocus = do_QueryInterface(selectControl);
595
0
                }
596
0
              }
597
0
            } else {
598
0
              elementToFocus = content;
599
0
            }
600
0
            if (elementToFocus) {
601
0
              fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY);
602
0
603
0
              // Return true if the element became focused.
604
0
              nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
605
0
              focused = (window && window->GetFocusedElement());
606
0
            }
607
0
          }
608
0
        }
609
0
        if (aKeyCausesActivation &&
610
0
            !content->IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::menulist)) {
611
0
          elm->ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD, aIsTrustedEvent);
612
0
        }
613
0
    } else {
614
0
        return content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
615
0
    }
616
0
617
0
    return focused;
618
0
}
619
620
//----------------------------------------------------------------------
621
622
void
623
nsXULElement::AddListenerFor(const nsAttrName& aName)
624
0
{
625
0
    // If appropriate, add a popup listener and/or compile the event
626
0
    // handler. Called when we change the element's document, create a
627
0
    // new element, change an attribute's value, etc.
628
0
    // Eventlistenener-attributes are always in the null namespace
629
0
    if (aName.IsAtom()) {
630
0
        nsAtom *attr = aName.Atom();
631
0
        MaybeAddPopupListener(attr);
632
0
        if (nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) {
633
0
            nsAutoString value;
634
0
            GetAttr(kNameSpaceID_None, attr, value);
635
0
            SetEventHandler(attr, value, true);
636
0
        }
637
0
    }
638
0
}
639
640
void
641
nsXULElement::MaybeAddPopupListener(nsAtom* aLocalName)
642
0
{
643
0
    // If appropriate, add a popup listener. Called when we change the
644
0
    // element's document, create a new element, change an attribute's
645
0
    // value, etc.
646
0
    if (aLocalName == nsGkAtoms::menu ||
647
0
        aLocalName == nsGkAtoms::contextmenu ||
648
0
        // XXXdwh popup and context are deprecated
649
0
        aLocalName == nsGkAtoms::popup ||
650
0
        aLocalName == nsGkAtoms::context) {
651
0
        AddPopupListener(aLocalName);
652
0
    }
653
0
}
654
655
//----------------------------------------------------------------------
656
//
657
// nsIContent interface
658
//
659
void
660
nsXULElement::UpdateEditableState(bool aNotify)
661
0
{
662
0
    // Don't call through to Element here because the things
663
0
    // it does don't work for cases when we're an editable control.
664
0
    nsIContent *parent = GetParent();
665
0
666
0
    SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
667
0
    UpdateState(aNotify);
668
0
}
669
670
class XULInContentErrorReporter : public Runnable
671
{
672
public:
673
  explicit XULInContentErrorReporter(nsIDocument* aDocument)
674
    : mozilla::Runnable("XULInContentErrorReporter")
675
    , mDocument(aDocument)
676
0
  {
677
0
  }
678
679
  NS_IMETHOD Run() override
680
0
  {
681
0
    mDocument->WarnOnceAbout(nsIDocument::eImportXULIntoContent, false);
682
0
    return NS_OK;
683
0
  }
684
685
private:
686
  nsCOMPtr<nsIDocument> mDocument;
687
};
688
689
static bool
690
NeedTooltipSupport(const nsXULElement& aXULElement)
691
0
{
692
0
  if (aXULElement.NodeInfo()->Equals(nsGkAtoms::treechildren)) {
693
0
    // treechildren always get tooltip support, since cropped tree cells show
694
0
    // their full text in a tooltip.
695
0
    return true;
696
0
  }
697
0
698
0
  return aXULElement.GetBoolAttr(nsGkAtoms::tooltip) ||
699
0
         aXULElement.GetBoolAttr(nsGkAtoms::tooltiptext);
700
0
}
701
702
nsresult
703
nsXULElement::BindToTree(nsIDocument* aDocument,
704
                         nsIContent* aParent,
705
                         nsIContent* aBindingParent)
706
0
{
707
0
  if (!aBindingParent &&
708
0
      aDocument &&
709
0
      !aDocument->IsLoadedAsInteractiveData() &&
710
0
      !aDocument->AllowXULXBL() &&
711
0
      !aDocument->HasWarnedAbout(nsIDocument::eImportXULIntoContent)) {
712
0
    nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(aDocument));
713
0
  }
714
0
715
0
  nsresult rv = nsStyledElement::BindToTree(aDocument, aParent, aBindingParent);
716
0
  NS_ENSURE_SUCCESS(rv, rv);
717
0
718
0
  nsIDocument* doc = GetComposedDoc();
719
#ifdef DEBUG
720
  if (doc && !doc->AllowXULXBL() && !doc->IsUnstyledDocument()) {
721
    // To save CPU cycles and memory, non-XUL documents only load the user
722
    // agent style sheet rules for a minimal set of XUL elements such as
723
    // 'scrollbar' that may be created implicitly for their content (those
724
    // rules being in minimal-xul.css).
725
    //
726
    // This assertion makes sure no other XUL element is used in a non-XUL
727
    // document.
728
    nsAtom* tag = NodeInfo()->NameAtom();
729
    MOZ_ASSERT(
730
      // scrollbar parts
731
      tag == nsGkAtoms::scrollbar ||
732
      tag == nsGkAtoms::scrollbarbutton ||
733
      tag == nsGkAtoms::scrollcorner ||
734
      tag == nsGkAtoms::slider ||
735
      tag == nsGkAtoms::thumb ||
736
      // other
737
      tag == nsGkAtoms::datetimebox ||
738
      tag == nsGkAtoms::resizer ||
739
      tag == nsGkAtoms::label ||
740
      tag == nsGkAtoms::videocontrols,
741
      "Unexpected XUL element in non-XUL doc"
742
    );
743
  }
744
#endif
745
746
0
  if (doc && NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
747
0
    // Create our XUL key listener and hook it up.
748
0
    nsXBLService::AttachGlobalKeyHandler(this);
749
0
  }
750
0
751
0
  if (doc && NeedTooltipSupport(*this)) {
752
0
      AddTooltipSupport();
753
0
  }
754
0
755
0
  return rv;
756
0
}
757
758
void
759
nsXULElement::UnbindFromTree(bool aDeep, bool aNullParent)
760
0
{
761
0
    if (NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
762
0
        nsXBLService::DetachGlobalKeyHandler(this);
763
0
    }
764
0
765
0
    if (NeedTooltipSupport(*this)) {
766
0
        RemoveTooltipSupport();
767
0
    }
768
0
769
0
    // mControllers can own objects that are implemented
770
0
    // in JavaScript (such as some implementations of
771
0
    // nsIControllers.  These objects prevent their global
772
0
    // object's script object from being garbage collected,
773
0
    // which means JS continues to hold an owning reference
774
0
    // to the nsGlobalWindow, which owns the document,
775
0
    // which owns this content.  That's a cycle, so we break
776
0
    // it here.  (It might be better to break this by releasing
777
0
    // mDocument in nsGlobalWindow::SetDocShell, but I'm not
778
0
    // sure whether that would fix all possible cycles through
779
0
    // mControllers.)
780
0
    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
781
0
    if (slots) {
782
0
        slots->mControllers = nullptr;
783
0
    }
784
0
785
0
    nsStyledElement::UnbindFromTree(aDeep, aNullParent);
786
0
}
787
788
void
789
nsXULElement::UnregisterAccessKey(const nsAString& aOldValue)
790
0
{
791
0
    // If someone changes the accesskey, unregister the old one
792
0
    //
793
0
    nsIDocument* doc = GetComposedDoc();
794
0
    if (doc && !aOldValue.IsEmpty()) {
795
0
        nsIPresShell *shell = doc->GetShell();
796
0
797
0
        if (shell) {
798
0
            Element* element = this;
799
0
800
0
            // find out what type of content node this is
801
0
            if (mNodeInfo->Equals(nsGkAtoms::label)) {
802
0
                // For anonymous labels the unregistering must
803
0
                // occur on the binding parent control.
804
0
                // XXXldb: And what if the binding parent is null?
805
0
                nsIContent* bindingParent = GetBindingParent();
806
0
                element = bindingParent ? bindingParent->AsElement() : nullptr;
807
0
            }
808
0
809
0
            if (element) {
810
0
                shell->GetPresContext()->EventStateManager()->
811
0
                    UnregisterAccessKey(element, aOldValue.First());
812
0
            }
813
0
        }
814
0
    }
815
0
}
816
817
nsresult
818
nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
819
                            const nsAttrValueOrString* aValue, bool aNotify)
820
0
{
821
0
    if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey &&
822
0
        IsInUncomposedDoc()) {
823
0
        nsAutoString oldValue;
824
0
        if (GetAttr(aNamespaceID, aName, oldValue)) {
825
0
            UnregisterAccessKey(oldValue);
826
0
        }
827
0
    } else if (aNamespaceID == kNameSpaceID_None &&
828
0
               (aName == nsGkAtoms::command || aName == nsGkAtoms::observes) &&
829
0
               IsInUncomposedDoc()) {
830
0
//         XXX sXBL/XBL2 issue! Owner or current document?
831
0
        nsAutoString oldValue;
832
0
        GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue);
833
0
        if (oldValue.IsEmpty()) {
834
0
          GetAttr(kNameSpaceID_None, nsGkAtoms::command, oldValue);
835
0
        }
836
0
837
0
        if (!oldValue.IsEmpty()) {
838
0
          RemoveBroadcaster(oldValue);
839
0
        }
840
0
    } else if (aNamespaceID == kNameSpaceID_None &&
841
0
               aValue &&
842
0
               mNodeInfo->Equals(nsGkAtoms::window) &&
843
0
               aName == nsGkAtoms::chromemargin) {
844
0
      nsAttrValue attrValue;
845
0
      // Make sure the margin format is valid first
846
0
      if (!attrValue.ParseIntMarginValue(aValue->String())) {
847
0
        return NS_ERROR_INVALID_ARG;
848
0
      }
849
0
    } else if (aNamespaceID == kNameSpaceID_None &&
850
0
               aName == nsGkAtoms::usercontextid) {
851
0
        nsAutoString oldValue;
852
0
        bool hasAttribute = GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, oldValue);
853
0
        if (hasAttribute && (!aValue || !aValue->String().Equals(oldValue))) {
854
0
          MOZ_ASSERT(false, "Changing usercontextid is not allowed.");
855
0
          return NS_ERROR_INVALID_ARG;
856
0
        }
857
0
    }
858
0
859
0
    return nsStyledElement::BeforeSetAttr(aNamespaceID, aName,
860
0
                                          aValue, aNotify);
861
0
}
862
863
nsresult
864
nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
865
                           const nsAttrValue* aValue,
866
                           const nsAttrValue* aOldValue,
867
                           nsIPrincipal* aSubjectPrincipal,
868
                           bool aNotify)
869
0
{
870
0
    if (aNamespaceID == kNameSpaceID_None) {
871
0
        if (aValue) {
872
0
            // Add popup and event listeners. We can't call AddListenerFor since
873
0
            // the attribute isn't set yet.
874
0
            MaybeAddPopupListener(aName);
875
0
            if (nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL)) {
876
0
                if (aValue->Type() == nsAttrValue::eString) {
877
0
                    SetEventHandler(aName, aValue->GetStringValue(), true);
878
0
                } else {
879
0
                    nsAutoString body;
880
0
                    aValue->ToString(body);
881
0
                    SetEventHandler(aName, body, true);
882
0
                }
883
0
            }
884
0
885
0
            nsIDocument* document = GetUncomposedDoc();
886
0
887
0
            // Hide chrome if needed
888
0
            if (mNodeInfo->Equals(nsGkAtoms::window)) {
889
0
                if (aName == nsGkAtoms::hidechrome) {
890
0
                    HideWindowChrome(
891
0
                      aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
892
0
                } else if (aName == nsGkAtoms::chromemargin) {
893
0
                    SetChromeMargins(aValue);
894
0
                } else if (aName == nsGkAtoms::windowtype &&
895
0
                           document && document->GetRootElement() == this) {
896
0
                    MaybeUpdatePrivateLifetime();
897
0
                }
898
0
            }
899
0
            // title and drawintitlebar are settable on
900
0
            // any root node (windows, dialogs, etc)
901
0
            if (document && document->GetRootElement() == this) {
902
0
                if (aName == nsGkAtoms::title) {
903
0
                    document->NotifyPossibleTitleChange(false);
904
0
                } else if (aName == nsGkAtoms::drawintitlebar) {
905
0
                    SetDrawsInTitlebar(
906
0
                        aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
907
0
                } else if (aName == nsGkAtoms::drawtitle) {
908
0
                    SetDrawsTitle(
909
0
                        aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
910
0
                } else if (aName == nsGkAtoms::localedir) {
911
0
                    // if the localedir changed on the root element, reset the document direction
912
0
                    if (document->IsXULDocument()) {
913
0
                        document->AsXULDocument()->ResetDocumentDirection();
914
0
                    }
915
0
                } else if (aName == nsGkAtoms::lwtheme ||
916
0
                         aName == nsGkAtoms::lwthemetextcolor) {
917
0
                    // if the lwtheme changed, make sure to reset the document lwtheme cache
918
0
                    document->ResetDocumentLWTheme();
919
0
                    UpdateBrightTitlebarForeground(document);
920
0
                } else if (aName == nsGkAtoms::brighttitlebarforeground) {
921
0
                    UpdateBrightTitlebarForeground(document);
922
0
                }
923
0
            }
924
0
        } else {
925
0
            if (mNodeInfo->Equals(nsGkAtoms::window)) {
926
0
                if (aName == nsGkAtoms::hidechrome) {
927
0
                    HideWindowChrome(false);
928
0
                } else if (aName == nsGkAtoms::chromemargin) {
929
0
                    ResetChromeMargins();
930
0
                }
931
0
            }
932
0
933
0
            nsIDocument* doc = GetUncomposedDoc();
934
0
            if (doc && doc->GetRootElement() == this) {
935
0
                if (aName == nsGkAtoms::localedir) {
936
0
                    // if the localedir changed on the root element, reset the document direction
937
0
                    if (doc->IsXULDocument()) {
938
0
                        doc->AsXULDocument()->ResetDocumentDirection();
939
0
                    }
940
0
                } else if ((aName == nsGkAtoms::lwtheme ||
941
0
                            aName == nsGkAtoms::lwthemetextcolor)) {
942
0
                    // if the lwtheme changed, make sure to restyle appropriately
943
0
                    doc->ResetDocumentLWTheme();
944
0
                    UpdateBrightTitlebarForeground(doc);
945
0
                } else if (aName == nsGkAtoms::brighttitlebarforeground) {
946
0
                    UpdateBrightTitlebarForeground(doc);
947
0
                } else if (aName == nsGkAtoms::drawintitlebar) {
948
0
                    SetDrawsInTitlebar(false);
949
0
                } else if (aName == nsGkAtoms::drawtitle) {
950
0
                    SetDrawsTitle(false);
951
0
                }
952
0
            }
953
0
        }
954
0
955
0
        if (aName == nsGkAtoms::tooltip || aName == nsGkAtoms::tooltiptext) {
956
0
            if (!!aValue != !!aOldValue &&
957
0
                IsInComposedDoc() &&
958
0
                !NodeInfo()->Equals(nsGkAtoms::treechildren)) {
959
0
                if (aValue) {
960
0
                    AddTooltipSupport();
961
0
                } else {
962
0
                    RemoveTooltipSupport();
963
0
                }
964
0
            }
965
0
        }
966
0
        // XXX need to check if they're changing an event handler: if
967
0
        // so, then we need to unhook the old one.  Or something.
968
0
    }
969
0
970
0
    return nsStyledElement::AfterSetAttr(aNamespaceID, aName,
971
0
                                         aValue, aOldValue, aSubjectPrincipal, aNotify);
972
0
}
973
974
void
975
nsXULElement::AddTooltipSupport()
976
0
{
977
0
  nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
978
0
  if (!listener) {
979
0
    return;
980
0
  }
981
0
982
0
  listener->AddTooltipSupport(this);
983
0
}
984
985
void
986
nsXULElement::RemoveTooltipSupport()
987
0
{
988
0
  nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
989
0
  if (!listener) {
990
0
    return;
991
0
  }
992
0
993
0
  listener->RemoveTooltipSupport(this);
994
0
}
995
996
bool
997
nsXULElement::ParseAttribute(int32_t aNamespaceID,
998
                             nsAtom* aAttribute,
999
                             const nsAString& aValue,
1000
                             nsIPrincipal* aMaybeScriptedPrincipal,
1001
                             nsAttrValue& aResult)
1002
0
{
1003
0
    // Parse into a nsAttrValue
1004
0
    if (!nsStyledElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
1005
0
                                         aMaybeScriptedPrincipal, aResult)) {
1006
0
        // Fall back to parsing as atom for short values
1007
0
        aResult.ParseStringOrAtom(aValue);
1008
0
    }
1009
0
1010
0
    return true;
1011
0
}
1012
1013
void
1014
nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId)
1015
0
{
1016
0
    nsIDocument* doc = OwnerDoc();
1017
0
    if (!doc->IsXULDocument()) {
1018
0
      return;
1019
0
    }
1020
0
    if (Element* broadcaster = doc->GetElementById(broadcasterId)) {
1021
0
        doc->AsXULDocument()->RemoveBroadcastListenerFor(
1022
0
           *broadcaster, *this, NS_LITERAL_STRING("*"));
1023
0
    }
1024
0
}
1025
1026
void
1027
nsXULElement::DestroyContent()
1028
0
{
1029
0
    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
1030
0
    if (slots) {
1031
0
        slots->mControllers = nullptr;
1032
0
    }
1033
0
1034
0
    nsStyledElement::DestroyContent();
1035
0
}
1036
1037
#ifdef DEBUG
1038
void
1039
nsXULElement::List(FILE* out, int32_t aIndent) const
1040
{
1041
    nsCString prefix("XUL");
1042
    if (HasSlots()) {
1043
      prefix.Append('*');
1044
    }
1045
    prefix.Append(' ');
1046
1047
    nsStyledElement::List(out, aIndent, prefix);
1048
}
1049
#endif
1050
1051
bool
1052
nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage)
1053
0
{
1054
0
    return (IsRootOfNativeAnonymousSubtree() &&
1055
0
            IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) &&
1056
0
            (aMessage == eMouseClick || aMessage == eMouseDoubleClick ||
1057
0
             aMessage == eXULCommand || aMessage == eContextMenu ||
1058
0
             aMessage == eDragStart  || aMessage == eMouseAuxClick));
1059
0
}
1060
1061
nsresult
1062
nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor,
1063
                                 nsAutoString& aCommand)
1064
0
{
1065
0
    // XXX sXBL/XBL2 issue! Owner or current document?
1066
0
    nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
1067
0
    NS_ENSURE_STATE(doc);
1068
0
    RefPtr<Element> commandElt = doc->GetElementById(aCommand);
1069
0
    if (commandElt) {
1070
0
        // Create a new command event to dispatch to the element
1071
0
        // pointed to by the command attribute. The new event's
1072
0
        // sourceEvent will be the original command event that we're
1073
0
        // handling.
1074
0
        RefPtr<Event> event = aVisitor.mDOMEvent;
1075
0
        uint16_t inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
1076
0
        while (event) {
1077
0
            NS_ENSURE_STATE(event->GetOriginalTarget() != commandElt);
1078
0
            RefPtr<XULCommandEvent> commandEvent = event->AsXULCommandEvent();
1079
0
            if (commandEvent) {
1080
0
                event = commandEvent->GetSourceEvent();
1081
0
                inputSource = commandEvent->InputSource();
1082
0
            } else {
1083
0
                event = nullptr;
1084
0
            }
1085
0
        }
1086
0
        WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent();
1087
0
        nsContentUtils::DispatchXULCommand(
1088
0
          commandElt,
1089
0
          orig->IsTrusted(),
1090
0
          aVisitor.mDOMEvent,
1091
0
          nullptr,
1092
0
          orig->IsControl(),
1093
0
          orig->IsAlt(),
1094
0
          orig->IsShift(),
1095
0
          orig->IsMeta(),
1096
0
          inputSource);
1097
0
    } else {
1098
0
        NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
1099
0
    }
1100
0
    return NS_OK;
1101
0
}
1102
1103
void
1104
nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
1105
0
{
1106
0
    aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119
1107
0
    if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) {
1108
0
        // Don't propagate these events from native anonymous scrollbar.
1109
0
        aVisitor.mCanHandle = true;
1110
0
        aVisitor.SetParentTarget(nullptr, false);
1111
0
        return;
1112
0
    }
1113
0
    if (aVisitor.mEvent->mMessage == eXULCommand &&
1114
0
        aVisitor.mEvent->mClass == eInputEventClass &&
1115
0
        aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this) &&
1116
0
        !IsXULElement(nsGkAtoms::command)) {
1117
0
        // Check that we really have an xul command event. That will be handled
1118
0
        // in a special way.
1119
0
        // See if we have a command elt.  If so, we execute on the command
1120
0
        // instead of on our content element.
1121
0
        nsAutoString command;
1122
0
        if (aVisitor.mDOMEvent &&
1123
0
            aVisitor.mDOMEvent->AsXULCommandEvent() &&
1124
0
            GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) &&
1125
0
            !command.IsEmpty()) {
1126
0
            // Stop building the event target chain for the original event.
1127
0
            // We don't want it to propagate to any DOM nodes.
1128
0
            aVisitor.mCanHandle = false;
1129
0
            aVisitor.mAutomaticChromeDispatch = false;
1130
0
            // Dispatch XUL command in PreHandleEvent to prevent it breaks event
1131
0
            // target chain creation
1132
0
            aVisitor.mWantsPreHandleEvent = true;
1133
0
            aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND;
1134
0
            return;
1135
0
        }
1136
0
    }
1137
0
1138
0
    nsStyledElement::GetEventTargetParent(aVisitor);
1139
0
}
1140
1141
nsresult
1142
nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor)
1143
0
{
1144
0
    if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) {
1145
0
        nsAutoString command;
1146
0
        GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
1147
0
        MOZ_ASSERT(!command.IsEmpty());
1148
0
        return DispatchXULCommand(aVisitor, command);
1149
0
    }
1150
0
    return nsStyledElement::PreHandleEvent(aVisitor);
1151
0
}
1152
1153
//----------------------------------------------------------------------
1154
// Implementation methods
1155
1156
1157
nsChangeHint
1158
nsXULElement::GetAttributeChangeHint(const nsAtom* aAttribute,
1159
                                     int32_t aModType) const
1160
0
{
1161
0
    nsChangeHint retval(nsChangeHint(0));
1162
0
1163
0
    if (aAttribute == nsGkAtoms::value &&
1164
0
        (aModType == MutationEvent_Binding::REMOVAL ||
1165
0
         aModType == MutationEvent_Binding::ADDITION)) {
1166
0
      if (IsAnyOfXULElements(nsGkAtoms::label, nsGkAtoms::description))
1167
0
        // Label and description dynamically morph between a normal
1168
0
        // block and a cropping single-line XUL text frame.  If the
1169
0
        // value attribute is being added or removed, then we need to
1170
0
        // return a hint of frame change.  (See bugzilla bug 95475 for
1171
0
        // details.)
1172
0
        retval = nsChangeHint_ReconstructFrame;
1173
0
    } else {
1174
0
        // if left or top changes we reflow. This will happen in xul
1175
0
        // containers that manage positioned children such as a stack.
1176
0
        if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute ||
1177
0
            nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute ||
1178
0
            nsGkAtoms::start == aAttribute || nsGkAtoms::end == aAttribute)
1179
0
            retval = NS_STYLE_HINT_REFLOW;
1180
0
    }
1181
0
1182
0
    return retval;
1183
0
}
1184
1185
NS_IMETHODIMP_(bool)
1186
nsXULElement::IsAttributeMapped(const nsAtom* aAttribute) const
1187
0
{
1188
0
    return false;
1189
0
}
1190
1191
nsIControllers*
1192
nsXULElement::GetControllers(ErrorResult& rv)
1193
0
{
1194
0
    if (! Controllers()) {
1195
0
        nsExtendedDOMSlots* slots = ExtendedDOMSlots();
1196
0
1197
0
        slots->mControllers = new nsXULControllers();
1198
0
    }
1199
0
1200
0
    return Controllers();
1201
0
}
1202
1203
already_AddRefed<BoxObject>
1204
nsXULElement::GetBoxObject(ErrorResult& rv)
1205
0
{
1206
0
    // XXX sXBL/XBL2 issue! Owner or current document?
1207
0
    return OwnerDoc()->GetBoxObjectFor(this, rv);
1208
0
}
1209
1210
void
1211
nsXULElement::Click(CallerType aCallerType)
1212
0
{
1213
0
  ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
1214
0
                       aCallerType == CallerType::System);
1215
0
}
1216
1217
void
1218
nsXULElement::ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent)
1219
0
{
1220
0
    if (BoolAttrIsTrue(nsGkAtoms::disabled))
1221
0
        return;
1222
0
1223
0
    nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // Strong just in case
1224
0
    if (doc) {
1225
0
        RefPtr<nsPresContext> context = doc->GetPresContext();
1226
0
        if (context) {
1227
0
            // strong ref to PresContext so events don't destroy it
1228
0
1229
0
            WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown,
1230
0
                                       nullptr, WidgetMouseEvent::eReal);
1231
0
            WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp,
1232
0
                                     nullptr, WidgetMouseEvent::eReal);
1233
0
            WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr,
1234
0
                                        WidgetMouseEvent::eReal);
1235
0
            eventDown.inputSource = eventUp.inputSource = eventClick.inputSource
1236
0
                                  = aInputSource;
1237
0
1238
0
            // send mouse down
1239
0
            nsEventStatus status = nsEventStatus_eIgnore;
1240
0
            EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
1241
0
                                      context, &eventDown,  nullptr, &status);
1242
0
1243
0
            // send mouse up
1244
0
            status = nsEventStatus_eIgnore;  // reset status
1245
0
            EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
1246
0
                                      context, &eventUp, nullptr, &status);
1247
0
1248
0
            // send mouse click
1249
0
            status = nsEventStatus_eIgnore;  // reset status
1250
0
            EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
1251
0
                                      context, &eventClick, nullptr, &status);
1252
0
1253
0
            // If the click has been prevented, lets skip the command call
1254
0
            // this is how a physical click works
1255
0
            if (status == nsEventStatus_eConsumeNoDefault) {
1256
0
                return;
1257
0
            }
1258
0
        }
1259
0
    }
1260
0
1261
0
    // oncommand is fired when an element is clicked...
1262
0
    DoCommand();
1263
0
}
1264
1265
void
1266
nsXULElement::DoCommand()
1267
0
{
1268
0
    nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // strong just in case
1269
0
    if (doc) {
1270
0
        nsContentUtils::DispatchXULCommand(this, true);
1271
0
    }
1272
0
}
1273
1274
bool
1275
nsXULElement::IsNodeOfType(uint32_t aFlags) const
1276
0
{
1277
0
    return false;
1278
0
}
1279
1280
nsresult
1281
nsXULElement::AddPopupListener(nsAtom* aName)
1282
0
{
1283
0
    // Add a popup listener to the element
1284
0
    bool isContext = (aName == nsGkAtoms::context ||
1285
0
                        aName == nsGkAtoms::contextmenu);
1286
0
    uint32_t listenerFlag = isContext ?
1287
0
                            XUL_ELEMENT_HAS_CONTENTMENU_LISTENER :
1288
0
                            XUL_ELEMENT_HAS_POPUP_LISTENER;
1289
0
1290
0
    if (HasFlag(listenerFlag)) {
1291
0
        return NS_OK;
1292
0
    }
1293
0
1294
0
    nsCOMPtr<nsIDOMEventListener> listener =
1295
0
      new nsXULPopupListener(this, isContext);
1296
0
1297
0
    // Add the popup as a listener on this element.
1298
0
    EventListenerManager* manager = GetOrCreateListenerManager();
1299
0
    SetFlags(listenerFlag);
1300
0
1301
0
    if (isContext) {
1302
0
      manager->AddEventListenerByType(listener,
1303
0
                                      NS_LITERAL_STRING("contextmenu"),
1304
0
                                      TrustedEventsAtSystemGroupBubble());
1305
0
    } else {
1306
0
      manager->AddEventListenerByType(listener,
1307
0
                                      NS_LITERAL_STRING("mousedown"),
1308
0
                                      TrustedEventsAtSystemGroupBubble());
1309
0
    }
1310
0
    return NS_OK;
1311
0
}
1312
1313
EventStates
1314
nsXULElement::IntrinsicState() const
1315
0
{
1316
0
    EventStates state = nsStyledElement::IntrinsicState();
1317
0
1318
0
    if (IsReadWriteTextElement()) {
1319
0
        state |= NS_EVENT_STATE_MOZ_READWRITE;
1320
0
        state &= ~NS_EVENT_STATE_MOZ_READONLY;
1321
0
    }
1322
0
1323
0
    return state;
1324
0
}
1325
1326
//----------------------------------------------------------------------
1327
1328
nsresult
1329
nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype)
1330
0
{
1331
0
    if (!aPrototype) {
1332
0
        return NS_OK;
1333
0
    }
1334
0
1335
0
    uint32_t i;
1336
0
    nsresult rv;
1337
0
    for (i = 0; i < aPrototype->mNumAttributes; ++i) {
1338
0
        nsXULPrototypeAttribute* protoattr = &aPrototype->mAttributes[i];
1339
0
        nsAttrValue attrValue;
1340
0
1341
0
        // Style rules need to be cloned.
1342
0
        if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) {
1343
0
            DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue();
1344
0
            RefPtr<DeclarationBlock> declClone = decl->Clone();
1345
0
1346
0
            nsString stringValue;
1347
0
            protoattr->mValue.ToString(stringValue);
1348
0
1349
0
            attrValue.SetTo(declClone.forget(), &stringValue);
1350
0
        } else {
1351
0
            attrValue.SetTo(protoattr->mValue);
1352
0
        }
1353
0
1354
0
        bool oldValueSet;
1355
0
        // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName
1356
0
        if (protoattr->mName.IsAtom()) {
1357
0
            rv = mAttrs.SetAndSwapAttr(protoattr->mName.Atom(),
1358
0
                                       attrValue, &oldValueSet);
1359
0
        } else {
1360
0
            rv = mAttrs.SetAndSwapAttr(protoattr->mName.NodeInfo(),
1361
0
                                       attrValue, &oldValueSet);
1362
0
        }
1363
0
        NS_ENSURE_SUCCESS(rv, rv);
1364
0
    }
1365
0
    return NS_OK;
1366
0
}
1367
1368
nsresult
1369
nsXULElement::HideWindowChrome(bool aShouldHide)
1370
0
{
1371
0
    nsIDocument* doc = GetUncomposedDoc();
1372
0
    if (!doc || doc->GetRootElement() != this)
1373
0
      return NS_ERROR_UNEXPECTED;
1374
0
1375
0
    // only top level chrome documents can hide the window chrome
1376
0
    if (!doc->IsRootDisplayDocument())
1377
0
      return NS_OK;
1378
0
1379
0
    nsPresContext* presContext = doc->GetPresContext();
1380
0
1381
0
    if (presContext && presContext->IsChrome()) {
1382
0
        nsIFrame* frame = GetPrimaryFrame();
1383
0
1384
0
        if (frame) {
1385
0
            nsView* view = frame->GetClosestView();
1386
0
1387
0
            if (view) {
1388
0
                nsIWidget* w = view->GetWidget();
1389
0
                NS_ENSURE_STATE(w);
1390
0
                w->HideWindowChrome(aShouldHide);
1391
0
            }
1392
0
        }
1393
0
    }
1394
0
1395
0
    return NS_OK;
1396
0
}
1397
1398
nsIWidget*
1399
nsXULElement::GetWindowWidget()
1400
0
{
1401
0
    nsIDocument* doc = GetComposedDoc();
1402
0
1403
0
    // only top level chrome documents can set the titlebar color
1404
0
    if (doc && doc->IsRootDisplayDocument()) {
1405
0
        nsCOMPtr<nsISupports> container = doc->GetContainer();
1406
0
        nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
1407
0
        if (baseWindow) {
1408
0
            nsCOMPtr<nsIWidget> mainWidget;
1409
0
            baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
1410
0
            return mainWidget;
1411
0
        }
1412
0
    }
1413
0
    return nullptr;
1414
0
}
1415
1416
class SetDrawInTitleBarEvent : public Runnable
1417
{
1418
public:
1419
  SetDrawInTitleBarEvent(nsIWidget* aWidget, bool aState)
1420
    : mozilla::Runnable("SetDrawInTitleBarEvent")
1421
    , mWidget(aWidget)
1422
    , mState(aState)
1423
0
  {}
1424
1425
0
  NS_IMETHOD Run() override {
1426
0
    NS_ASSERTION(mWidget, "You shouldn't call this runnable with a null widget!");
1427
0
1428
0
    mWidget->SetDrawsInTitlebar(mState);
1429
0
    return NS_OK;
1430
0
  }
1431
1432
private:
1433
  nsCOMPtr<nsIWidget> mWidget;
1434
  bool mState;
1435
};
1436
1437
void
1438
nsXULElement::SetDrawsInTitlebar(bool aState)
1439
0
{
1440
0
    nsIWidget* mainWidget = GetWindowWidget();
1441
0
    if (mainWidget) {
1442
0
        nsContentUtils::AddScriptRunner(new SetDrawInTitleBarEvent(mainWidget, aState));
1443
0
    }
1444
0
}
1445
1446
void
1447
nsXULElement::SetDrawsTitle(bool aState)
1448
0
{
1449
0
    nsIWidget* mainWidget = GetWindowWidget();
1450
0
    if (mainWidget) {
1451
0
        // We can do this synchronously because SetDrawsTitle doesn't have any
1452
0
        // synchronous effects apart from a harmless invalidation.
1453
0
        mainWidget->SetDrawsTitle(aState);
1454
0
    }
1455
0
}
1456
1457
void
1458
nsXULElement::UpdateBrightTitlebarForeground(nsIDocument* aDoc)
1459
0
{
1460
0
    nsIWidget* mainWidget = GetWindowWidget();
1461
0
    if (mainWidget) {
1462
0
        // We can do this synchronously because SetBrightTitlebarForeground doesn't have any
1463
0
        // synchronous effects apart from a harmless invalidation.
1464
0
        mainWidget->SetUseBrightTitlebarForeground(
1465
0
          aDoc->GetDocumentLWTheme() == nsIDocument::Doc_Theme_Bright ||
1466
0
          aDoc->GetRootElement()->AttrValueIs(kNameSpaceID_None,
1467
0
                                              nsGkAtoms::brighttitlebarforeground,
1468
0
                                              NS_LITERAL_STRING("true"),
1469
0
                                              eCaseMatters));
1470
0
    }
1471
0
}
1472
1473
class MarginSetter : public Runnable
1474
{
1475
public:
1476
  explicit MarginSetter(nsIWidget* aWidget)
1477
    : mozilla::Runnable("MarginSetter")
1478
    , mWidget(aWidget)
1479
    , mMargin(-1, -1, -1, -1)
1480
0
  {
1481
0
  }
1482
  MarginSetter(nsIWidget* aWidget, const LayoutDeviceIntMargin& aMargin)
1483
    : mozilla::Runnable("MarginSetter")
1484
    , mWidget(aWidget)
1485
    , mMargin(aMargin)
1486
0
  {
1487
0
  }
1488
1489
  NS_IMETHOD Run() override
1490
0
  {
1491
0
    // SetNonClientMargins can dispatch native events, hence doing
1492
0
    // it off a script runner.
1493
0
    mWidget->SetNonClientMargins(mMargin);
1494
0
    return NS_OK;
1495
0
    }
1496
1497
private:
1498
    nsCOMPtr<nsIWidget> mWidget;
1499
    LayoutDeviceIntMargin mMargin;
1500
};
1501
1502
void
1503
nsXULElement::SetChromeMargins(const nsAttrValue* aValue)
1504
0
{
1505
0
    if (!aValue)
1506
0
        return;
1507
0
1508
0
    nsIWidget* mainWidget = GetWindowWidget();
1509
0
    if (!mainWidget)
1510
0
        return;
1511
0
1512
0
    // top, right, bottom, left - see nsAttrValue
1513
0
    nsIntMargin margins;
1514
0
    bool gotMargins = false;
1515
0
1516
0
    if (aValue->Type() == nsAttrValue::eIntMarginValue) {
1517
0
        gotMargins = aValue->GetIntMarginValue(margins);
1518
0
    } else {
1519
0
        nsAutoString tmp;
1520
0
        aValue->ToString(tmp);
1521
0
        gotMargins = nsContentUtils::ParseIntMarginValue(tmp, margins);
1522
0
    }
1523
0
    if (gotMargins) {
1524
0
        nsContentUtils::AddScriptRunner(
1525
0
            new MarginSetter(
1526
0
                mainWidget, LayoutDeviceIntMargin::FromUnknownMargin(margins)));
1527
0
    }
1528
0
}
1529
1530
void
1531
nsXULElement::ResetChromeMargins()
1532
0
{
1533
0
    nsIWidget* mainWidget = GetWindowWidget();
1534
0
    if (!mainWidget)
1535
0
        return;
1536
0
    // See nsIWidget
1537
0
    nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget));
1538
0
}
1539
1540
bool
1541
nsXULElement::BoolAttrIsTrue(nsAtom* aName) const
1542
0
{
1543
0
    const nsAttrValue* attr =
1544
0
        GetAttrInfo(kNameSpaceID_None, aName).mValue;
1545
0
1546
0
    return attr && attr->Type() == nsAttrValue::eAtom &&
1547
0
           attr->GetAtomValue() == nsGkAtoms::_true;
1548
0
}
1549
1550
void
1551
nsXULElement::RecompileScriptEventListeners()
1552
0
{
1553
0
    int32_t i, count = mAttrs.AttrCount();
1554
0
    for (i = 0; i < count; ++i) {
1555
0
        const nsAttrName *name = mAttrs.AttrNameAt(i);
1556
0
1557
0
        // Eventlistenener-attributes are always in the null namespace
1558
0
        if (!name->IsAtom()) {
1559
0
            continue;
1560
0
        }
1561
0
1562
0
        nsAtom *attr = name->Atom();
1563
0
        if (!nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) {
1564
0
            continue;
1565
0
        }
1566
0
1567
0
        nsAutoString value;
1568
0
        GetAttr(kNameSpaceID_None, attr, value);
1569
0
        SetEventHandler(attr, value, true);
1570
0
    }
1571
0
}
1572
1573
bool
1574
nsXULElement::IsEventAttributeNameInternal(nsAtom *aName)
1575
0
{
1576
0
  return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL);
1577
0
}
1578
1579
JSObject*
1580
nsXULElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
1581
0
{
1582
0
    return dom::XULElement_Binding::Wrap(aCx, this, aGivenProto);
1583
0
}
1584
1585
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
1586
1587
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode)
1588
0
    if (tmp->mType == nsXULPrototypeNode::eType_Element) {
1589
0
        static_cast<nsXULPrototypeElement*>(tmp)->Unlink();
1590
0
    } else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
1591
0
        static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects();
1592
0
    }
1593
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1594
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode)
1595
0
    if (tmp->mType == nsXULPrototypeNode::eType_Element) {
1596
0
        nsXULPrototypeElement *elem =
1597
0
            static_cast<nsXULPrototypeElement*>(tmp);
1598
0
        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo");
1599
0
        cb.NoteNativeChild(elem->mNodeInfo,
1600
0
                           NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1601
0
        uint32_t i;
1602
0
        for (i = 0; i < elem->mNumAttributes; ++i) {
1603
0
            const nsAttrName& name = elem->mAttributes[i].mName;
1604
0
            if (!name.IsAtom()) {
1605
0
                NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1606
0
                    "mAttributes[i].mName.NodeInfo()");
1607
0
                cb.NoteNativeChild(name.NodeInfo(),
1608
0
                                   NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1609
0
            }
1610
0
        }
1611
0
        ImplCycleCollectionTraverse(cb, elem->mChildren, "mChildren");
1612
0
    }
1613
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1614
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode)
1615
0
    if (tmp->mType == nsXULPrototypeNode::eType_Script) {
1616
0
        nsXULPrototypeScript *script =
1617
0
            static_cast<nsXULPrototypeScript*>(tmp);
1618
0
        script->Trace(aCallbacks, aClosure);
1619
0
    }
1620
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
1621
1622
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
1623
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release)
1624
1625
//----------------------------------------------------------------------
1626
//
1627
// nsXULPrototypeAttribute
1628
//
1629
1630
nsXULPrototypeAttribute::~nsXULPrototypeAttribute()
1631
0
{
1632
0
    MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
1633
0
}
1634
1635
1636
//----------------------------------------------------------------------
1637
//
1638
// nsXULPrototypeElement
1639
//
1640
1641
nsresult
1642
nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream,
1643
                                 nsXULPrototypeDocument* aProtoDoc,
1644
                                 const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
1645
0
{
1646
0
    nsresult rv;
1647
0
1648
0
    // Write basic prototype data
1649
0
    rv = aStream->Write32(mType);
1650
0
1651
0
    // Write Node Info
1652
0
    int32_t index = aNodeInfos->IndexOf(mNodeInfo);
1653
0
    NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
1654
0
    nsresult tmp = aStream->Write32(index);
1655
0
    if (NS_FAILED(tmp)) {
1656
0
      rv = tmp;
1657
0
    }
1658
0
1659
0
    // Write Attributes
1660
0
    tmp = aStream->Write32(mNumAttributes);
1661
0
    if (NS_FAILED(tmp)) {
1662
0
      rv = tmp;
1663
0
    }
1664
0
1665
0
    nsAutoString attributeValue;
1666
0
    uint32_t i;
1667
0
    for (i = 0; i < mNumAttributes; ++i) {
1668
0
        RefPtr<mozilla::dom::NodeInfo> ni;
1669
0
        if (mAttributes[i].mName.IsAtom()) {
1670
0
            ni = mNodeInfo->NodeInfoManager()->
1671
0
                GetNodeInfo(mAttributes[i].mName.Atom(), nullptr,
1672
0
                            kNameSpaceID_None, nsINode::ATTRIBUTE_NODE);
1673
0
            NS_ASSERTION(ni, "the nodeinfo should already exist");
1674
0
        } else {
1675
0
            ni = mAttributes[i].mName.NodeInfo();
1676
0
        }
1677
0
1678
0
        index = aNodeInfos->IndexOf(ni);
1679
0
        NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
1680
0
        tmp = aStream->Write32(index);
1681
0
        if (NS_FAILED(tmp)) {
1682
0
          rv = tmp;
1683
0
        }
1684
0
1685
0
        mAttributes[i].mValue.ToString(attributeValue);
1686
0
        tmp = aStream->WriteWStringZ(attributeValue.get());
1687
0
        if (NS_FAILED(tmp)) {
1688
0
          rv = tmp;
1689
0
        }
1690
0
    }
1691
0
1692
0
    // Now write children
1693
0
    tmp = aStream->Write32(uint32_t(mChildren.Length()));
1694
0
    if (NS_FAILED(tmp)) {
1695
0
      rv = tmp;
1696
0
    }
1697
0
    for (i = 0; i < mChildren.Length(); i++) {
1698
0
        nsXULPrototypeNode* child = mChildren[i].get();
1699
0
        switch (child->mType) {
1700
0
        case eType_Element:
1701
0
        case eType_Text:
1702
0
        case eType_PI:
1703
0
            tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos);
1704
0
            if (NS_FAILED(tmp)) {
1705
0
              rv = tmp;
1706
0
            }
1707
0
            break;
1708
0
        case eType_Script:
1709
0
            tmp = aStream->Write32(child->mType);
1710
0
            if (NS_FAILED(tmp)) {
1711
0
              rv = tmp;
1712
0
            }
1713
0
            nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(child);
1714
0
1715
0
            tmp = aStream->Write8(script->mOutOfLine);
1716
0
            if (NS_FAILED(tmp)) {
1717
0
              rv = tmp;
1718
0
            }
1719
0
            if (! script->mOutOfLine) {
1720
0
                tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos);
1721
0
                if (NS_FAILED(tmp)) {
1722
0
                  rv = tmp;
1723
0
                }
1724
0
            } else {
1725
0
                tmp = aStream->WriteCompoundObject(script->mSrcURI,
1726
0
                                                   NS_GET_IID(nsIURI),
1727
0
                                                   true);
1728
0
                if (NS_FAILED(tmp)) {
1729
0
                  rv = tmp;
1730
0
                }
1731
0
1732
0
                if (script->HasScriptObject()) {
1733
0
                    // This may return NS_OK without muxing script->mSrcURI's
1734
0
                    // data into the cache file, in the case where that
1735
0
                    // muxed document is already there (written by a prior
1736
0
                    // session, or by an earlier cache episode during this
1737
0
                    // session).
1738
0
                    tmp = script->SerializeOutOfLine(aStream, aProtoDoc);
1739
0
                    if (NS_FAILED(tmp)) {
1740
0
                      rv = tmp;
1741
0
                    }
1742
0
                }
1743
0
            }
1744
0
            break;
1745
0
        }
1746
0
    }
1747
0
1748
0
    return rv;
1749
0
}
1750
1751
nsresult
1752
nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
1753
                                   nsXULPrototypeDocument* aProtoDoc,
1754
                                   nsIURI* aDocumentURI,
1755
                                   const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
1756
0
{
1757
0
    MOZ_ASSERT(aNodeInfos, "missing nodeinfo array");
1758
0
1759
0
    // Read Node Info
1760
0
    uint32_t number = 0;
1761
0
    nsresult rv = aStream->Read32(&number);
1762
0
    if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1763
0
    mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr);
1764
0
    if (!mNodeInfo) {
1765
0
        return NS_ERROR_UNEXPECTED;
1766
0
    }
1767
0
1768
0
    // Read Attributes
1769
0
    rv = aStream->Read32(&number);
1770
0
    if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1771
0
    mNumAttributes = int32_t(number);
1772
0
1773
0
    if (mNumAttributes > 0) {
1774
0
        mAttributes = new (fallible) nsXULPrototypeAttribute[mNumAttributes];
1775
0
        if (!mAttributes) {
1776
0
            return NS_ERROR_OUT_OF_MEMORY;
1777
0
        }
1778
0
1779
0
        nsAutoString attributeValue;
1780
0
        for (uint32_t i = 0; i < mNumAttributes; ++i) {
1781
0
            rv = aStream->Read32(&number);
1782
0
            if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1783
0
            mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr);
1784
0
            if (!ni) {
1785
0
                return NS_ERROR_UNEXPECTED;
1786
0
            }
1787
0
1788
0
            mAttributes[i].mName.SetTo(ni);
1789
0
1790
0
            rv = aStream->ReadString(attributeValue);
1791
0
            if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1792
0
            rv = SetAttrAt(i, attributeValue, aDocumentURI);
1793
0
            if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1794
0
        }
1795
0
    }
1796
0
1797
0
    rv = aStream->Read32(&number);
1798
0
    if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1799
0
    uint32_t numChildren = int32_t(number);
1800
0
1801
0
    if (numChildren > 0) {
1802
0
        if (!mChildren.SetCapacity(numChildren, fallible)) {
1803
0
            return NS_ERROR_OUT_OF_MEMORY;
1804
0
        }
1805
0
1806
0
        for (uint32_t i = 0; i < numChildren; i++) {
1807
0
            rv = aStream->Read32(&number);
1808
0
            if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1809
0
            Type childType = (Type)number;
1810
0
1811
0
            RefPtr<nsXULPrototypeNode> child;
1812
0
1813
0
            switch (childType) {
1814
0
            case eType_Element:
1815
0
                child = new nsXULPrototypeElement();
1816
0
                rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
1817
0
                                        aNodeInfos);
1818
0
                if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1819
0
                break;
1820
0
            case eType_Text:
1821
0
                child = new nsXULPrototypeText();
1822
0
                rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
1823
0
                                        aNodeInfos);
1824
0
                if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1825
0
                break;
1826
0
            case eType_PI:
1827
0
                child = new nsXULPrototypePI();
1828
0
                rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
1829
0
                                        aNodeInfos);
1830
0
                if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1831
0
                break;
1832
0
            case eType_Script: {
1833
0
                // language version/options obtained during deserialization.
1834
0
                RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0);
1835
0
1836
0
                rv = aStream->ReadBoolean(&script->mOutOfLine);
1837
0
                if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1838
0
                if (!script->mOutOfLine) {
1839
0
                    rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
1840
0
                                             aNodeInfos);
1841
0
                    if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1842
0
                } else {
1843
0
                    nsCOMPtr<nsISupports> supports;
1844
0
                    rv = aStream->ReadObject(true, getter_AddRefs(supports));
1845
0
                    if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1846
0
                    script->mSrcURI = do_QueryInterface(supports);
1847
0
1848
0
                    rv = script->DeserializeOutOfLine(aStream, aProtoDoc);
1849
0
                    if (NS_WARN_IF(NS_FAILED(rv))) return rv;
1850
0
                }
1851
0
1852
0
                child = script.forget();
1853
0
                break;
1854
0
            }
1855
0
            default:
1856
0
                MOZ_ASSERT(false, "Unexpected child type!");
1857
0
                return NS_ERROR_UNEXPECTED;
1858
0
            }
1859
0
1860
0
            MOZ_ASSERT(child, "Don't append null to mChildren");
1861
0
            MOZ_ASSERT(child->mType == childType);
1862
0
            mChildren.AppendElement(child);
1863
0
1864
0
            // Oh dear. Something failed during the deserialization.
1865
0
            // We don't know what.  But likely consequences of failed
1866
0
            // deserializations included calls to |AbortCaching| which
1867
0
            // shuts down the cache and closes our streams.
1868
0
            // If that happens, next time through this loop, we die a messy
1869
0
            // death. So, let's just fail now, and propagate that failure
1870
0
            // upward so that the ChromeProtocolHandler knows it can't use
1871
0
            // a cached chrome channel for this.
1872
0
            if (NS_WARN_IF(NS_FAILED(rv)))
1873
0
                return rv;
1874
0
        }
1875
0
    }
1876
0
1877
0
    return rv;
1878
0
}
1879
1880
nsresult
1881
nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
1882
                                 nsIURI* aDocumentURI)
1883
0
{
1884
0
    MOZ_ASSERT(aPos < mNumAttributes, "out-of-bounds");
1885
0
1886
0
    // WARNING!!
1887
0
    // This code is largely duplicated in nsXULElement::SetAttr.
1888
0
    // Any changes should be made to both functions.
1889
0
1890
0
    if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
1891
0
        mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
1892
0
1893
0
        return NS_OK;
1894
0
    }
1895
0
1896
0
    if (mAttributes[aPos].mName.Equals(nsGkAtoms::id) &&
1897
0
        !aValue.IsEmpty()) {
1898
0
        mHasIdAttribute = true;
1899
0
        // Store id as atom.
1900
0
        // id="" means that the element has no id. Not that it has
1901
0
        // emptystring as id.
1902
0
        mAttributes[aPos].mValue.ParseAtom(aValue);
1903
0
1904
0
        return NS_OK;
1905
0
    } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
1906
0
        // Store is as atom.
1907
0
        mAttributes[aPos].mValue.ParseAtom(aValue);
1908
0
        mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
1909
0
1910
0
        return NS_OK;
1911
0
    } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) {
1912
0
        mHasClassAttribute = true;
1913
0
        // Compute the element's class list
1914
0
        mAttributes[aPos].mValue.ParseAtomArray(aValue);
1915
0
1916
0
        return NS_OK;
1917
0
    } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) {
1918
0
        mHasStyleAttribute = true;
1919
0
        // Parse the element's 'style' attribute
1920
0
1921
0
        // This is basically duplicating what nsINode::NodePrincipal() does
1922
0
        nsIPrincipal* principal =
1923
0
          mNodeInfo->NodeInfoManager()->DocumentPrincipal();
1924
0
        // XXX Get correct Base URI (need GetBaseURI on *prototype* element)
1925
0
        // TODO: If we implement Content Security Policy for chrome documents
1926
0
        // as has been discussed, the CSP should be checked here to see if
1927
0
        // inline styles are allowed to be applied.
1928
0
        // XXX No specific specs talk about xul and referrer policy, pass Unset
1929
0
        RefPtr<URLExtraData> data =
1930
0
          new URLExtraData(aDocumentURI, aDocumentURI, principal,
1931
0
                           mozilla::net::RP_Unset);
1932
0
        RefPtr<DeclarationBlock> declaration =
1933
0
          DeclarationBlock::FromCssText(
1934
0
            aValue, data, eCompatibility_FullStandards, nullptr);
1935
0
        if (declaration) {
1936
0
            mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue);
1937
0
1938
0
            return NS_OK;
1939
0
        }
1940
0
        // Don't abort if parsing failed, it could just be malformed css.
1941
0
    }
1942
0
1943
0
    mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
1944
0
1945
0
    return NS_OK;
1946
0
}
1947
1948
void
1949
nsXULPrototypeElement::Unlink()
1950
0
{
1951
0
    mNumAttributes = 0;
1952
0
    delete[] mAttributes;
1953
0
    mAttributes = nullptr;
1954
0
    mChildren.Clear();
1955
0
}
1956
1957
void
1958
nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc)
1959
0
{
1960
0
    for (uint32_t i = 0; i < mChildren.Length(); ++i) {
1961
0
        nsXULPrototypeNode* child = mChildren[i];
1962
0
        if (child->mType == nsXULPrototypeNode::eType_Element) {
1963
0
            static_cast<nsXULPrototypeElement*>(child)->TraceAllScripts(aTrc);
1964
0
        } else if (child->mType == nsXULPrototypeNode::eType_Script) {
1965
0
            static_cast<nsXULPrototypeScript*>(child)->TraceScriptObject(aTrc);
1966
0
        }
1967
0
    }
1968
0
}
1969
1970
//----------------------------------------------------------------------
1971
//
1972
// nsXULPrototypeScript
1973
//
1974
1975
nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo)
1976
    : nsXULPrototypeNode(eType_Script),
1977
      mLineNo(aLineNo),
1978
      mSrcLoading(false),
1979
      mOutOfLine(true),
1980
      mSrcLoadWaiters(nullptr),
1981
      mScriptObject(nullptr)
1982
0
{
1983
0
}
1984
1985
1986
nsXULPrototypeScript::~nsXULPrototypeScript()
1987
0
{
1988
0
    UnlinkJSObjects();
1989
0
}
1990
1991
nsresult
1992
nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
1993
                                nsXULPrototypeDocument* aProtoDoc,
1994
                                const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
1995
0
{
1996
0
    NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED);
1997
0
1998
0
    AutoJSAPI jsapi;
1999
0
    if (!jsapi.Init(xpc::CompilationScope())) {
2000
0
        return NS_ERROR_UNEXPECTED;
2001
0
    }
2002
0
2003
0
    NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
2004
0
                 !mScriptObject,
2005
0
                 "script source still loading when serializing?!");
2006
0
    if (!mScriptObject)
2007
0
        return NS_ERROR_FAILURE;
2008
0
2009
0
    // Write basic prototype data
2010
0
    nsresult rv;
2011
0
    rv = aStream->Write32(mLineNo);
2012
0
    if (NS_FAILED(rv)) return rv;
2013
0
    rv = aStream->Write32(0); // See bug 1418294.
2014
0
    if (NS_FAILED(rv)) return rv;
2015
0
2016
0
    JSContext* cx = jsapi.cx();
2017
0
    JS::Rooted<JSScript*> script(cx, mScriptObject);
2018
0
    MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
2019
0
    return nsContentUtils::XPConnect()->WriteScript(aStream, cx, script);
2020
0
}
2021
2022
nsresult
2023
nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
2024
                                         nsXULPrototypeDocument* aProtoDoc)
2025
0
{
2026
0
    nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
2027
0
2028
0
    bool isChrome = false;
2029
0
    if (NS_FAILED(mSrcURI->SchemeIs("chrome", &isChrome)) || !isChrome)
2030
0
       // Don't cache scripts that don't come from chrome uris.
2031
0
       return rv;
2032
0
2033
0
    nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
2034
0
    if (!cache)
2035
0
        return NS_ERROR_OUT_OF_MEMORY;
2036
0
2037
0
    NS_ASSERTION(cache->IsEnabled(),
2038
0
                 "writing to the cache file, but the XUL cache is off?");
2039
0
    bool exists;
2040
0
    cache->HasData(mSrcURI, &exists);
2041
0
2042
0
    /* return will be NS_OK from GetAsciiSpec.
2043
0
     * that makes no sense.
2044
0
     * nor does returning NS_OK from HasMuxedDocument.
2045
0
     * XXX return something meaningful.
2046
0
     */
2047
0
    if (exists)
2048
0
        return NS_OK;
2049
0
2050
0
    nsCOMPtr<nsIObjectOutputStream> oos;
2051
0
    rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
2052
0
    NS_ENSURE_SUCCESS(rv, rv);
2053
0
2054
0
    nsresult tmp = Serialize(oos, aProtoDoc, nullptr);
2055
0
    if (NS_FAILED(tmp)) {
2056
0
      rv = tmp;
2057
0
    }
2058
0
    tmp = cache->FinishOutputStream(mSrcURI);
2059
0
    if (NS_FAILED(tmp)) {
2060
0
      rv = tmp;
2061
0
    }
2062
0
2063
0
    if (NS_FAILED(rv))
2064
0
        cache->AbortCaching();
2065
0
    return rv;
2066
0
}
2067
2068
2069
nsresult
2070
nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
2071
                                  nsXULPrototypeDocument* aProtoDoc,
2072
                                  nsIURI* aDocumentURI,
2073
                                  const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
2074
0
{
2075
0
    nsresult rv;
2076
0
    NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
2077
0
                 !mScriptObject,
2078
0
                 "prototype script not well-initialized when deserializing?!");
2079
0
2080
0
    // Read basic prototype data
2081
0
    rv = aStream->Read32(&mLineNo);
2082
0
    if (NS_FAILED(rv)) return rv;
2083
0
    uint32_t dummy;
2084
0
    rv = aStream->Read32(&dummy); // See bug 1418294.
2085
0
    if (NS_FAILED(rv)) return rv;
2086
0
2087
0
    AutoJSAPI jsapi;
2088
0
    if (!jsapi.Init(xpc::CompilationScope())) {
2089
0
        return NS_ERROR_UNEXPECTED;
2090
0
    }
2091
0
    JSContext* cx = jsapi.cx();
2092
0
2093
0
    JS::Rooted<JSScript*> newScriptObject(cx);
2094
0
    rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
2095
0
                                                 newScriptObject.address());
2096
0
    NS_ENSURE_SUCCESS(rv, rv);
2097
0
    Set(newScriptObject);
2098
0
    return NS_OK;
2099
0
}
2100
2101
2102
nsresult
2103
nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
2104
                                           nsXULPrototypeDocument* aProtoDoc)
2105
0
{
2106
0
    // Keep track of failure via rv, so we can
2107
0
    // AbortCaching if things look bad.
2108
0
    nsresult rv = NS_OK;
2109
0
    nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
2110
0
2111
0
    nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
2112
0
    if (cache) {
2113
0
        bool useXULCache = true;
2114
0
        if (mSrcURI) {
2115
0
            // NB: we must check the XUL script cache early, to avoid
2116
0
            // multiple deserialization attempts for a given script.
2117
0
            // Note that XULDocument::LoadScript
2118
0
            // checks the XUL script cache too, in order to handle the
2119
0
            // serialization case.
2120
0
            //
2121
0
            // We need do this only for <script src='strres.js'> and the
2122
0
            // like, i.e., out-of-line scripts that are included by several
2123
0
            // different XUL documents stored in the cache file.
2124
0
            useXULCache = cache->IsEnabled();
2125
0
2126
0
            if (useXULCache) {
2127
0
                JSScript* newScriptObject =
2128
0
                    cache->GetScript(mSrcURI);
2129
0
                if (newScriptObject)
2130
0
                    Set(newScriptObject);
2131
0
            }
2132
0
        }
2133
0
2134
0
        if (!mScriptObject) {
2135
0
            if (mSrcURI) {
2136
0
                rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput));
2137
0
            }
2138
0
            // If !mSrcURI, we have an inline script. We shouldn't have
2139
0
            // to do anything else in that case, I think.
2140
0
2141
0
            // We do reflect errors into rv, but our caller may want to
2142
0
            // ignore our return value, because mScriptObject will be null
2143
0
            // after any error, and that suffices to cause the script to
2144
0
            // be reloaded (from the src= URI, if any) and recompiled.
2145
0
            // We're better off slow-loading than bailing out due to a
2146
0
            // error.
2147
0
            if (NS_SUCCEEDED(rv))
2148
0
                rv = Deserialize(objectInput, aProtoDoc, nullptr, nullptr);
2149
0
2150
0
            if (NS_SUCCEEDED(rv)) {
2151
0
                if (useXULCache && mSrcURI) {
2152
0
                    bool isChrome = false;
2153
0
                    mSrcURI->SchemeIs("chrome", &isChrome);
2154
0
                    if (isChrome) {
2155
0
                        JS::Rooted<JSScript*> script(RootingCx(), GetScriptObject());
2156
0
                        cache->PutScript(mSrcURI, script);
2157
0
                    }
2158
0
                }
2159
0
                cache->FinishInputStream(mSrcURI);
2160
0
            } else {
2161
0
                // If mSrcURI is not in the cache,
2162
0
                // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
2163
0
                // update the cache file to hold a serialization of
2164
0
                // this script, once it has finished loading.
2165
0
                if (rv != NS_ERROR_NOT_AVAILABLE)
2166
0
                    cache->AbortCaching();
2167
0
            }
2168
0
        }
2169
0
    }
2170
0
    return rv;
2171
0
}
2172
2173
class NotifyOffThreadScriptCompletedRunnable : public Runnable
2174
{
2175
    // An array of all outstanding script receivers. All reference counting of
2176
    // these objects happens on the main thread. When we return to the main
2177
    // thread from script compilation we make sure our receiver is still in
2178
    // this array (still alive) before proceeding. This array is cleared during
2179
    // shutdown, potentially before all outstanding script compilations have
2180
    // finished. We do not need to worry about pointer replay here, because
2181
    // a) we should not be starting script compilation after clearing this
2182
    // array and b) in all other cases the receiver will still be alive.
2183
    static StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> sReceivers;
2184
    static bool sSetupClearOnShutdown;
2185
2186
    nsIOffThreadScriptReceiver* mReceiver;
2187
    JS::OffThreadToken* mToken;
2188
2189
public:
2190
  NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver* aReceiver,
2191
                                         JS::OffThreadToken* aToken)
2192
    : mozilla::Runnable("NotifyOffThreadScriptCompletedRunnable")
2193
    , mReceiver(aReceiver)
2194
    , mToken(aToken)
2195
0
  {
2196
0
  }
2197
2198
  static void NoteReceiver(nsIOffThreadScriptReceiver* aReceiver)
2199
0
  {
2200
0
    if (!sSetupClearOnShutdown) {
2201
0
      ClearOnShutdown(&sReceivers);
2202
0
      sSetupClearOnShutdown = true;
2203
0
      sReceivers = new nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>();
2204
0
    }
2205
0
2206
0
    // If we ever crash here, it's because we tried to lazy compile script
2207
0
    // too late in shutdown.
2208
0
    sReceivers->AppendElement(aReceiver);
2209
0
    }
2210
2211
    NS_DECL_NSIRUNNABLE
2212
};
2213
2214
StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> NotifyOffThreadScriptCompletedRunnable::sReceivers;
2215
bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown = false;
2216
2217
NS_IMETHODIMP
2218
NotifyOffThreadScriptCompletedRunnable::Run()
2219
0
{
2220
0
    MOZ_ASSERT(NS_IsMainThread());
2221
0
2222
0
    JS::Rooted<JSScript*> script(RootingCx());
2223
0
    {
2224
0
        AutoJSAPI jsapi;
2225
0
        if (!jsapi.Init(xpc::CompilationScope())) {
2226
0
            // Now what?  I guess we just leak... this should probably never
2227
0
            // happen.
2228
0
            return NS_ERROR_UNEXPECTED;
2229
0
        }
2230
0
        JSContext* cx = jsapi.cx();
2231
0
        script = JS::FinishOffThreadScript(cx, mToken);
2232
0
    }
2233
0
2234
0
    if (!sReceivers) {
2235
0
        // We've already shut down.
2236
0
        return NS_OK;
2237
0
    }
2238
0
2239
0
    auto index = sReceivers->IndexOf(mReceiver);
2240
0
    MOZ_RELEASE_ASSERT(index != sReceivers->NoIndex);
2241
0
    nsCOMPtr<nsIOffThreadScriptReceiver> receiver = (*sReceivers)[index].forget();
2242
0
    sReceivers->RemoveElementAt(index);
2243
0
2244
0
    return receiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
2245
0
}
2246
2247
static void
2248
OffThreadScriptReceiverCallback(JS::OffThreadToken* aToken, void* aCallbackData)
2249
0
{
2250
0
    // Be careful not to adjust the refcount on the receiver, as this callback
2251
0
    // may be invoked off the main thread.
2252
0
    nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData);
2253
0
    RefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
2254
0
        new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken);
2255
0
    NS_DispatchToMainThread(notify);
2256
0
}
2257
2258
nsresult
2259
nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
2260
                              nsIURI* aURI, uint32_t aLineNo,
2261
                              nsIDocument* aDocument,
2262
                              nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
2263
0
{
2264
0
    // We'll compile the script in the compilation scope.
2265
0
    AutoJSAPI jsapi;
2266
0
    if (!jsapi.Init(xpc::CompilationScope())) {
2267
0
        return NS_ERROR_UNEXPECTED;
2268
0
    }
2269
0
    JSContext* cx = jsapi.cx();
2270
0
2271
0
    nsresult rv;
2272
0
    nsAutoCString urlspec;
2273
0
    nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec, &rv);
2274
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2275
0
      return rv;
2276
0
    }
2277
0
2278
0
    // Ok, compile it to create a prototype script object!
2279
0
    JS::CompileOptions options(cx);
2280
0
    options.setIntroductionType("scriptElement")
2281
0
           .setFileAndLine(urlspec.get(), aLineNo);
2282
0
    // If the script was inline, tell the JS parser to save source for
2283
0
    // Function.prototype.toSource(). If it's out of line, we retrieve the
2284
0
    // source from the files on demand.
2285
0
    options.setSourceIsLazy(mOutOfLine);
2286
0
    JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
2287
0
    if (scope) {
2288
0
      JS::ExposeObjectToActiveJS(scope);
2289
0
    }
2290
0
2291
0
    if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
2292
0
        if (!JS::CompileOffThread(cx, options,
2293
0
                                  aSrcBuf,
2294
0
                                  OffThreadScriptReceiverCallback,
2295
0
                                  static_cast<void*>(aOffThreadReceiver))) {
2296
0
            return NS_ERROR_OUT_OF_MEMORY;
2297
0
        }
2298
0
        NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
2299
0
    } else {
2300
0
        JS::Rooted<JSScript*> script(cx);
2301
0
        if (!JS::Compile(cx, options, aSrcBuf, &script))
2302
0
            return NS_ERROR_OUT_OF_MEMORY;
2303
0
        Set(script);
2304
0
    }
2305
0
    return NS_OK;
2306
0
}
2307
2308
nsresult
2309
nsXULPrototypeScript::Compile(const char16_t* aText,
2310
                              int32_t aTextLength,
2311
                              nsIURI* aURI,
2312
                              uint32_t aLineNo,
2313
                              nsIDocument* aDocument,
2314
                              nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
2315
0
{
2316
0
  JS::SourceBufferHolder srcBuf(aText, aTextLength,
2317
0
                                JS::SourceBufferHolder::NoOwnership);
2318
0
  return Compile(srcBuf, aURI, aLineNo, aDocument, aOffThreadReceiver);
2319
0
}
2320
2321
void
2322
nsXULPrototypeScript::UnlinkJSObjects()
2323
0
{
2324
0
    if (mScriptObject) {
2325
0
        mScriptObject = nullptr;
2326
0
        mozilla::DropJSObjects(this);
2327
0
    }
2328
0
}
2329
2330
void
2331
nsXULPrototypeScript::Set(JSScript* aObject)
2332
0
{
2333
0
    MOZ_ASSERT(!mScriptObject, "Leaking script object.");
2334
0
    if (!aObject) {
2335
0
        mScriptObject = nullptr;
2336
0
        return;
2337
0
    }
2338
0
2339
0
    mScriptObject = aObject;
2340
0
    mozilla::HoldJSObjects(this);
2341
0
}
2342
2343
//----------------------------------------------------------------------
2344
//
2345
// nsXULPrototypeText
2346
//
2347
2348
nsresult
2349
nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream,
2350
                              nsXULPrototypeDocument* aProtoDoc,
2351
                              const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
2352
0
{
2353
0
    nsresult rv;
2354
0
2355
0
    // Write basic prototype data
2356
0
    rv = aStream->Write32(mType);
2357
0
2358
0
    nsresult tmp = aStream->WriteWStringZ(mValue.get());
2359
0
    if (NS_FAILED(tmp)) {
2360
0
      rv = tmp;
2361
0
    }
2362
0
2363
0
    return rv;
2364
0
}
2365
2366
nsresult
2367
nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
2368
                                nsXULPrototypeDocument* aProtoDoc,
2369
                                nsIURI* aDocumentURI,
2370
                                const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
2371
0
{
2372
0
    nsresult rv = aStream->ReadString(mValue);
2373
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2374
0
        return rv;
2375
0
    }
2376
0
    return NS_OK;
2377
0
}
2378
2379
//----------------------------------------------------------------------
2380
//
2381
// nsXULPrototypePI
2382
//
2383
2384
nsresult
2385
nsXULPrototypePI::Serialize(nsIObjectOutputStream* aStream,
2386
                            nsXULPrototypeDocument* aProtoDoc,
2387
                            const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
2388
0
{
2389
0
    nsresult rv;
2390
0
2391
0
    // Write basic prototype data
2392
0
    rv = aStream->Write32(mType);
2393
0
2394
0
    nsresult tmp = aStream->WriteWStringZ(mTarget.get());
2395
0
    if (NS_FAILED(tmp)) {
2396
0
      rv = tmp;
2397
0
    }
2398
0
    tmp = aStream->WriteWStringZ(mData.get());
2399
0
    if (NS_FAILED(tmp)) {
2400
0
      rv = tmp;
2401
0
    }
2402
0
2403
0
    return rv;
2404
0
}
2405
2406
nsresult
2407
nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream,
2408
                              nsXULPrototypeDocument* aProtoDoc,
2409
                              nsIURI* aDocumentURI,
2410
                              const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
2411
0
{
2412
0
    nsresult rv;
2413
0
2414
0
    rv = aStream->ReadString(mTarget);
2415
0
    if (NS_FAILED(rv)) return rv;
2416
0
    rv = aStream->ReadString(mData);
2417
0
    if (NS_FAILED(rv)) return rv;
2418
0
2419
0
    return rv;
2420
0
}