mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 03:05:34 +00:00
6a6ca1a08c
It's currently only accessible on XULDocument and XULElement, but that makes porting existing JS to run in an HTML document inconvenient. We could alternatively change calling JS, but this can be easily moved and exposed in chrome contexts. MozReview-Commit-ID: JitYET20NSE --HG-- extra : rebase_source : 75d823c688cba8d84dc19705e83284be383962f2
2388 lines
79 KiB
C++
2388 lines
79 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsDOMCID.h"
|
|
#include "nsError.h"
|
|
#include "nsDOMString.h"
|
|
#include "nsAtom.h"
|
|
#include "nsIBaseWindow.h"
|
|
#include "nsIDOMEventListener.h"
|
|
#include "nsIDOMXULCommandDispatcher.h"
|
|
#include "nsIDOMXULSelectCntrlItemEl.h"
|
|
#include "nsIDocument.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/EventListenerManager.h"
|
|
#include "mozilla/EventStateManager.h"
|
|
#include "mozilla/EventStates.h"
|
|
#include "mozilla/DeclarationBlock.h"
|
|
#include "nsFocusManager.h"
|
|
#include "nsHTMLStyleSheet.h"
|
|
#include "nsNameSpaceManager.h"
|
|
#include "nsIObjectInputStream.h"
|
|
#include "nsIObjectOutputStream.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIPrincipal.h"
|
|
#include "nsIScriptContext.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIURL.h"
|
|
#include "nsViewManager.h"
|
|
#include "nsIWidget.h"
|
|
#include "nsLayoutCID.h"
|
|
#include "nsContentCID.h"
|
|
#include "mozilla/dom/Event.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsString.h"
|
|
#include "nsXULControllers.h"
|
|
#include "nsIBoxObject.h"
|
|
#include "nsPIBoxObject.h"
|
|
#include "XULDocument.h"
|
|
#include "nsXULPopupListener.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsContentList.h"
|
|
#include "mozilla/InternalMutationEvent.h"
|
|
#include "mozilla/MouseEvents.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsJSPrincipals.h"
|
|
#include "nsDOMAttributeMap.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsNodeUtils.h"
|
|
#include "nsFrameLoader.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "nsIControllers.h"
|
|
#include "nsAttrValueOrString.h"
|
|
#include "nsAttrValueInlines.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "nsIController.h"
|
|
#include "nsQueryObject.h"
|
|
#include <algorithm>
|
|
#include "nsIDOMChromeWindow.h"
|
|
|
|
#include "nsReadableUtils.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsNodeInfoManager.h"
|
|
#include "nsXBLBinding.h"
|
|
#include "nsXULTooltipListener.h"
|
|
#include "mozilla/EventDispatcher.h"
|
|
#include "mozAutoDocUpdate.h"
|
|
#include "nsCCUncollectableMarker.h"
|
|
#include "nsICSSDeclaration.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "XULFrameElement.h"
|
|
#include "XULPopupElement.h"
|
|
#include "XULScrollElement.h"
|
|
|
|
#include "mozilla/dom/XULElementBinding.h"
|
|
#include "mozilla/dom/BoxObject.h"
|
|
#include "mozilla/dom/MouseEventBinding.h"
|
|
#include "mozilla/dom/MutationEventBinding.h"
|
|
#include "mozilla/dom/XULCommandEvent.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
|
|
uint32_t nsXULPrototypeAttribute::gNumElements;
|
|
uint32_t nsXULPrototypeAttribute::gNumAttributes;
|
|
uint32_t nsXULPrototypeAttribute::gNumCacheTests;
|
|
uint32_t nsXULPrototypeAttribute::gNumCacheHits;
|
|
uint32_t nsXULPrototypeAttribute::gNumCacheSets;
|
|
uint32_t nsXULPrototypeAttribute::gNumCacheFills;
|
|
#endif
|
|
|
|
#define NS_DISPATCH_XUL_COMMAND (1 << 0)
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsXULElement
|
|
//
|
|
|
|
nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
|
: nsStyledElement(aNodeInfo),
|
|
mBindingParent(nullptr)
|
|
{
|
|
XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
|
|
|
|
// We may be READWRITE by default; check.
|
|
if (IsReadWriteTextElement()) {
|
|
AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE);
|
|
RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
|
|
}
|
|
}
|
|
|
|
nsXULElement::~nsXULElement()
|
|
{
|
|
}
|
|
|
|
void
|
|
nsXULElement::MaybeUpdatePrivateLifetime()
|
|
{
|
|
if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype,
|
|
NS_LITERAL_STRING("navigator:browser"),
|
|
eCaseMatters)) {
|
|
return;
|
|
}
|
|
|
|
nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
|
|
nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr;
|
|
if (docShell) {
|
|
docShell->SetAffectPrivateSessionLifetime(false);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
nsXULElement* NS_NewBasicXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
{
|
|
return new nsXULElement(aNodeInfo);
|
|
}
|
|
|
|
/* static */
|
|
nsXULElement* nsXULElement::Construct(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
{
|
|
RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
|
|
if (nodeInfo->Equals(nsGkAtoms::menupopup) ||
|
|
nodeInfo->Equals(nsGkAtoms::popup) ||
|
|
nodeInfo->Equals(nsGkAtoms::panel) ||
|
|
nodeInfo->Equals(nsGkAtoms::tooltip)) {
|
|
return NS_NewXULPopupElement(nodeInfo.forget());
|
|
}
|
|
|
|
if (nodeInfo->Equals(nsGkAtoms::iframe) ||
|
|
nodeInfo->Equals(nsGkAtoms::browser) ||
|
|
nodeInfo->Equals(nsGkAtoms::editor)) {
|
|
already_AddRefed<mozilla::dom::NodeInfo> frameni = nodeInfo.forget();
|
|
return new XULFrameElement(frameni);
|
|
}
|
|
|
|
if (nodeInfo->Equals(nsGkAtoms::scrollbox)) {
|
|
already_AddRefed<mozilla::dom::NodeInfo> scrollni = nodeInfo.forget();
|
|
return new XULScrollElement(scrollni);
|
|
}
|
|
|
|
return NS_NewBasicXULElement(nodeInfo.forget());
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<nsXULElement>
|
|
nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
|
|
mozilla::dom::NodeInfo *aNodeInfo,
|
|
bool aIsScriptable,
|
|
bool aIsRoot)
|
|
{
|
|
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
|
|
nsCOMPtr<Element> baseElement;
|
|
NS_NewXULElement(getter_AddRefs(baseElement),
|
|
ni.forget(),
|
|
dom::FROM_PARSER_NETWORK,
|
|
aPrototype->mIsAtom);
|
|
|
|
if (baseElement) {
|
|
nsXULElement* element = FromNode(baseElement);
|
|
|
|
if (aPrototype->mHasIdAttribute) {
|
|
element->SetHasID();
|
|
}
|
|
if (aPrototype->mHasClassAttribute) {
|
|
element->SetMayHaveClass();
|
|
}
|
|
if (aPrototype->mHasStyleAttribute) {
|
|
element->SetMayHaveStyle();
|
|
}
|
|
|
|
element->MakeHeavyweight(aPrototype);
|
|
if (aIsScriptable) {
|
|
// Check each attribute on the prototype to see if we need to do
|
|
// any additional processing and hookup that would otherwise be
|
|
// done 'automagically' by SetAttr().
|
|
for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
|
|
element->AddListenerFor(aPrototype->mAttributes[i].mName,
|
|
true);
|
|
}
|
|
}
|
|
|
|
if (aIsRoot && aPrototype->mNodeInfo->Equals(nsGkAtoms::window)) {
|
|
for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
|
|
if (aPrototype->mAttributes[i].mName.Equals(nsGkAtoms::windowtype)) {
|
|
element->MaybeUpdatePrivateLifetime();
|
|
}
|
|
}
|
|
}
|
|
|
|
return baseElement.forget().downcast<nsXULElement>();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
|
|
nsIDocument* aDocument,
|
|
bool aIsScriptable,
|
|
bool aIsRoot,
|
|
Element** aResult)
|
|
{
|
|
// Create an nsXULElement from a prototype
|
|
MOZ_ASSERT(aPrototype != nullptr, "null ptr");
|
|
if (! aPrototype)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
MOZ_ASSERT(aResult != nullptr, "null ptr");
|
|
if (! aResult)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
RefPtr<mozilla::dom::NodeInfo> nodeInfo;
|
|
if (aDocument) {
|
|
mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo;
|
|
nodeInfo = aDocument->NodeInfoManager()->
|
|
GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(),
|
|
ELEMENT_NODE);
|
|
} else {
|
|
nodeInfo = aPrototype->mNodeInfo;
|
|
}
|
|
|
|
RefPtr<nsXULElement> element = CreateFromPrototype(aPrototype, nodeInfo,
|
|
aIsScriptable, aIsRoot);
|
|
element.forget(aResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
|
FromParser aFromParser, nsAtom* aIsAtom,
|
|
mozilla::dom::CustomElementDefinition* aDefinition)
|
|
{
|
|
RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
|
|
|
|
MOZ_ASSERT(nodeInfo, "need nodeinfo for non-proto Create");
|
|
|
|
NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
|
|
"Trying to create XUL elements that don't have the XUL namespace");
|
|
|
|
nsIDocument* doc = nodeInfo->GetDocument();
|
|
if (doc && !doc->AllowXULXBL()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIsAtom, aDefinition);
|
|
}
|
|
|
|
void
|
|
NS_TrustedNewXULElement(Element** aResult,
|
|
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
{
|
|
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
|
|
MOZ_ASSERT(ni, "need nodeinfo for non-proto Create");
|
|
|
|
// Create an nsXULElement with the specified namespace and tag.
|
|
NS_ADDREF(*aResult = nsXULElement::Construct(ni.forget()));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsISupports interface
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement,
|
|
nsStyledElement)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingParent);
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULElement,
|
|
nsStyledElement)
|
|
// Why aren't we unlinking the prototype?
|
|
tmp->ClearHasID();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBindingParent);
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement)
|
|
NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement)
|
|
|
|
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement)
|
|
NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
|
|
NS_INTERFACE_MAP_END_INHERITING(nsStyledElement)
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsINode interface
|
|
|
|
nsresult
|
|
nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
|
|
bool aPreallocateChildren) const
|
|
{
|
|
*aResult = nullptr;
|
|
|
|
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
|
|
RefPtr<nsXULElement> element = Construct(ni.forget());
|
|
|
|
nsresult rv = element->mAttrsAndChildren.EnsureCapacityToClone(mAttrsAndChildren,
|
|
aPreallocateChildren);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Note that we're _not_ copying mControllers.
|
|
|
|
uint32_t count = mAttrsAndChildren.AttrCount();
|
|
rv = NS_OK;
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
const nsAttrName* originalName = mAttrsAndChildren.AttrNameAt(i);
|
|
const nsAttrValue* originalValue = mAttrsAndChildren.AttrAt(i);
|
|
nsAttrValue attrValue;
|
|
|
|
// Style rules need to be cloned.
|
|
if (originalValue->Type() == nsAttrValue::eCSSDeclaration) {
|
|
DeclarationBlock* decl = originalValue->GetCSSDeclarationValue();
|
|
RefPtr<DeclarationBlock> declClone = decl->Clone();
|
|
|
|
nsString stringValue;
|
|
originalValue->ToString(stringValue);
|
|
|
|
attrValue.SetTo(declClone.forget(), &stringValue);
|
|
} else {
|
|
attrValue.SetTo(*originalValue);
|
|
}
|
|
|
|
bool oldValueSet;
|
|
if (originalName->IsAtom()) {
|
|
rv = element->mAttrsAndChildren.SetAndSwapAttr(originalName->Atom(),
|
|
attrValue,
|
|
&oldValueSet);
|
|
} else {
|
|
rv = element->mAttrsAndChildren.SetAndSwapAttr(originalName->NodeInfo(),
|
|
attrValue,
|
|
&oldValueSet);
|
|
}
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
element->AddListenerFor(*originalName, true);
|
|
if (originalName->Equals(nsGkAtoms::id) &&
|
|
!originalValue->IsEmptyString()) {
|
|
element->SetHasID();
|
|
}
|
|
if (originalName->Equals(nsGkAtoms::_class)) {
|
|
element->SetMayHaveClass();
|
|
}
|
|
if (originalName->Equals(nsGkAtoms::style)) {
|
|
element->SetMayHaveStyle();
|
|
}
|
|
}
|
|
|
|
element.forget(aResult);
|
|
return rv;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
EventListenerManager*
|
|
nsXULElement::GetEventListenerManagerForAttr(nsAtom* aAttrName, bool* aDefer)
|
|
{
|
|
// XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc()
|
|
// here, override BindToTree for those classes and munge event
|
|
// listeners there?
|
|
nsIDocument* doc = OwnerDoc();
|
|
|
|
nsPIDOMWindowInner *window;
|
|
Element *root = doc->GetRootElement();
|
|
if ((!root || root == this) && (window = doc->GetInnerWindow())) {
|
|
|
|
nsCOMPtr<EventTarget> piTarget = do_QueryInterface(window);
|
|
|
|
*aDefer = false;
|
|
return piTarget->GetOrCreateListenerManager();
|
|
}
|
|
|
|
return nsStyledElement::GetEventListenerManagerForAttr(aAttrName, aDefer);
|
|
}
|
|
|
|
// returns true if the element is not a list
|
|
static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo)
|
|
{
|
|
return !aNodeInfo->Equals(nsGkAtoms::tree) &&
|
|
!aNodeInfo->Equals(nsGkAtoms::richlistbox);
|
|
}
|
|
|
|
bool
|
|
nsXULElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse)
|
|
{
|
|
/*
|
|
* Returns true if an element may be focused, and false otherwise. The inout
|
|
* argument aTabIndex will be set to the tab order index to be used; -1 for
|
|
* elements that should not be part of the tab order and a greater value to
|
|
* indicate its tab order.
|
|
*
|
|
* Confusingly, the supplied value for the aTabIndex argument may indicate
|
|
* whether the element may be focused as a result of the -moz-user-focus
|
|
* property, where -1 means no and 0 means yes.
|
|
*
|
|
* For controls, the element cannot be focused and is not part of the tab
|
|
* order if it is disabled.
|
|
*
|
|
* Controls (those that implement nsIDOMXULControlElement):
|
|
* *aTabIndex = -1 no tabindex Not focusable or tabbable
|
|
* *aTabIndex = -1 tabindex="-1" Not focusable or tabbable
|
|
* *aTabIndex = -1 tabindex=">=0" Focusable and tabbable
|
|
* *aTabIndex >= 0 no tabindex Focusable and tabbable
|
|
* *aTabIndex >= 0 tabindex="-1" Focusable but not tabbable
|
|
* *aTabIndex >= 0 tabindex=">=0" Focusable and tabbable
|
|
* Non-controls:
|
|
* *aTabIndex = -1 Not focusable or tabbable
|
|
* *aTabIndex >= 0 Focusable and tabbable
|
|
*
|
|
* If aTabIndex is null, then the tabindex is not computed, and
|
|
* true is returned for non-disabled controls and false otherwise.
|
|
*/
|
|
|
|
// elements are not focusable by default
|
|
bool shouldFocus = false;
|
|
|
|
#ifdef XP_MACOSX
|
|
// on Mac, mouse interactions only focus the element if it's a list,
|
|
// or if it's a remote target, since the remote target must handle
|
|
// the focus.
|
|
if (aWithMouse &&
|
|
IsNonList(mNodeInfo) &&
|
|
!EventStateManager::IsRemoteTarget(this))
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this);
|
|
if (xulControl) {
|
|
// a disabled element cannot be focused and is not part of the tab order
|
|
bool disabled;
|
|
xulControl->GetDisabled(&disabled);
|
|
if (disabled) {
|
|
if (aTabIndex)
|
|
*aTabIndex = -1;
|
|
return false;
|
|
}
|
|
shouldFocus = true;
|
|
}
|
|
|
|
if (aTabIndex) {
|
|
if (xulControl) {
|
|
if (HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
|
|
// if either the aTabIndex argument or a specified tabindex is non-negative,
|
|
// the element becomes focusable.
|
|
int32_t tabIndex = 0;
|
|
xulControl->GetTabIndex(&tabIndex);
|
|
shouldFocus = *aTabIndex >= 0 || tabIndex >= 0;
|
|
*aTabIndex = tabIndex;
|
|
} else {
|
|
// otherwise, if there is no tabindex attribute, just use the value of
|
|
// *aTabIndex to indicate focusability. Reset any supplied tabindex to 0.
|
|
shouldFocus = *aTabIndex >= 0;
|
|
if (shouldFocus)
|
|
*aTabIndex = 0;
|
|
}
|
|
|
|
if (shouldFocus && sTabFocusModelAppliesToXUL &&
|
|
!(sTabFocusModel & eTabFocus_formElementsMask)) {
|
|
// By default, the tab focus model doesn't apply to xul element on any system but OS X.
|
|
// on OS X we're following it for UI elements (XUL) as sTabFocusModel is based on
|
|
// "Full Keyboard Access" system setting (see mac/nsILookAndFeel).
|
|
// both textboxes and list elements (i.e. trees and list) should always be focusable
|
|
// (textboxes are handled as html:input)
|
|
// For compatibility, we only do this for controls, otherwise elements like <browser>
|
|
// cannot take this focus.
|
|
if (IsNonList(mNodeInfo))
|
|
*aTabIndex = -1;
|
|
}
|
|
} else {
|
|
shouldFocus = *aTabIndex >= 0;
|
|
}
|
|
}
|
|
|
|
return shouldFocus;
|
|
}
|
|
|
|
bool
|
|
nsXULElement::PerformAccesskey(bool aKeyCausesActivation,
|
|
bool aIsTrustedEvent)
|
|
{
|
|
RefPtr<Element> content(this);
|
|
|
|
if (IsXULElement(nsGkAtoms::label)) {
|
|
nsAutoString control;
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::control, control);
|
|
if (control.IsEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
//XXXsmaug Should we use ShadowRoot::GetElementById in case
|
|
// content is in Shadow DOM?
|
|
nsCOMPtr<nsIDocument> document = content->GetUncomposedDoc();
|
|
if (!document) {
|
|
return false;
|
|
}
|
|
|
|
content = document->GetElementById(control);
|
|
if (!content) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
nsIFrame* frame = content->GetPrimaryFrame();
|
|
if (!frame || !frame->IsVisibleConsideringAncestors()) {
|
|
return false;
|
|
}
|
|
|
|
bool focused = false;
|
|
nsXULElement* elm = FromNode(content);
|
|
if (elm) {
|
|
// Define behavior for each type of XUL element.
|
|
if (!content->IsXULElement(nsGkAtoms::toolbarbutton)) {
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
if (fm) {
|
|
nsCOMPtr<Element> elementToFocus;
|
|
// for radio buttons, focus the radiogroup instead
|
|
if (content->IsXULElement(nsGkAtoms::radio)) {
|
|
nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(do_QueryInterface(content));
|
|
if (controlItem) {
|
|
bool disabled;
|
|
controlItem->GetDisabled(&disabled);
|
|
if (!disabled) {
|
|
nsCOMPtr<nsIDOMXULSelectControlElement> selectControl;
|
|
controlItem->GetControl(getter_AddRefs(selectControl));
|
|
elementToFocus = do_QueryInterface(selectControl);
|
|
}
|
|
}
|
|
} else {
|
|
elementToFocus = content;
|
|
}
|
|
if (elementToFocus) {
|
|
fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY);
|
|
|
|
// Return true if the element became focused.
|
|
nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
|
|
focused = (window && window->GetFocusedElement());
|
|
}
|
|
}
|
|
}
|
|
if (aKeyCausesActivation &&
|
|
!content->IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::menulist)) {
|
|
elm->ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD, aIsTrustedEvent);
|
|
}
|
|
} else {
|
|
return content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
|
|
}
|
|
|
|
return focused;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void
|
|
nsXULElement::AddListenerFor(const nsAttrName& aName,
|
|
bool aCompileEventHandlers)
|
|
{
|
|
// If appropriate, add a popup listener and/or compile the event
|
|
// handler. Called when we change the element's document, create a
|
|
// new element, change an attribute's value, etc.
|
|
// Eventlistenener-attributes are always in the null namespace
|
|
if (aName.IsAtom()) {
|
|
nsAtom *attr = aName.Atom();
|
|
MaybeAddPopupListener(attr);
|
|
if (aCompileEventHandlers &&
|
|
nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) {
|
|
nsAutoString value;
|
|
GetAttr(kNameSpaceID_None, attr, value);
|
|
SetEventHandler(attr, value, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULElement::MaybeAddPopupListener(nsAtom* aLocalName)
|
|
{
|
|
// If appropriate, add a popup listener. Called when we change the
|
|
// element's document, create a new element, change an attribute's
|
|
// value, etc.
|
|
if (aLocalName == nsGkAtoms::menu ||
|
|
aLocalName == nsGkAtoms::contextmenu ||
|
|
// XXXdwh popup and context are deprecated
|
|
aLocalName == nsGkAtoms::popup ||
|
|
aLocalName == nsGkAtoms::context) {
|
|
AddPopupListener(aLocalName);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// nsIContent interface
|
|
//
|
|
void
|
|
nsXULElement::UpdateEditableState(bool aNotify)
|
|
{
|
|
// Don't call through to Element here because the things
|
|
// it does don't work for cases when we're an editable control.
|
|
nsIContent *parent = GetParent();
|
|
|
|
SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
|
|
UpdateState(aNotify);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/**
|
|
* Returns true if the user-agent style sheet rules for this XUL element are
|
|
* in minimal-xul.css instead of xul.css.
|
|
*/
|
|
static inline bool XULElementsRulesInMinimalXULSheet(nsAtom* aTag)
|
|
{
|
|
return // scrollbar parts:
|
|
aTag == nsGkAtoms::scrollbar ||
|
|
aTag == nsGkAtoms::scrollbarbutton ||
|
|
aTag == nsGkAtoms::scrollcorner ||
|
|
aTag == nsGkAtoms::slider ||
|
|
aTag == nsGkAtoms::thumb ||
|
|
// other
|
|
aTag == nsGkAtoms::datetimebox ||
|
|
aTag == nsGkAtoms::resizer ||
|
|
aTag == nsGkAtoms::label ||
|
|
aTag == nsGkAtoms::videocontrols;
|
|
}
|
|
#endif
|
|
|
|
class XULInContentErrorReporter : public Runnable
|
|
{
|
|
public:
|
|
explicit XULInContentErrorReporter(nsIDocument* aDocument)
|
|
: mozilla::Runnable("XULInContentErrorReporter")
|
|
, mDocument(aDocument)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run() override
|
|
{
|
|
mDocument->WarnOnceAbout(nsIDocument::eImportXULIntoContent, false);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsCOMPtr<nsIDocument> mDocument;
|
|
};
|
|
|
|
static bool
|
|
NeedTooltipSupport(const nsXULElement& aXULElement)
|
|
{
|
|
if (aXULElement.NodeInfo()->Equals(nsGkAtoms::treechildren)) {
|
|
// treechildren always get tooltip support, since cropped tree cells show
|
|
// their full text in a tooltip.
|
|
return true;
|
|
}
|
|
|
|
return aXULElement.GetBoolAttr(nsGkAtoms::tooltip) ||
|
|
aXULElement.GetBoolAttr(nsGkAtoms::tooltiptext);
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::BindToTree(nsIDocument* aDocument,
|
|
nsIContent* aParent,
|
|
nsIContent* aBindingParent,
|
|
bool aCompileEventHandlers)
|
|
{
|
|
if (!aBindingParent &&
|
|
aDocument &&
|
|
!aDocument->IsLoadedAsInteractiveData() &&
|
|
!aDocument->AllowXULXBL() &&
|
|
!aDocument->HasWarnedAbout(nsIDocument::eImportXULIntoContent)) {
|
|
nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(aDocument));
|
|
}
|
|
|
|
nsresult rv = nsStyledElement::BindToTree(aDocument, aParent,
|
|
aBindingParent,
|
|
aCompileEventHandlers);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsIDocument* doc = GetComposedDoc();
|
|
#ifdef DEBUG
|
|
if (doc && !doc->AllowXULXBL() && !doc->IsUnstyledDocument()) {
|
|
// To save CPU cycles and memory, non-XUL documents only load the user
|
|
// agent style sheet rules for a minimal set of XUL elements such as
|
|
// 'scrollbar' that may be created implicitly for their content (those
|
|
// rules being in minimal-xul.css).
|
|
//
|
|
// This assertion makes sure no other XUL element than the ones in the
|
|
// minimal XUL sheet is used in the bindings.
|
|
if (!XULElementsRulesInMinimalXULSheet(NodeInfo()->NameAtom())) {
|
|
NS_ERROR("Unexpected XUL element in non-XUL doc");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (doc && NeedTooltipSupport(*this)) {
|
|
AddTooltipSupport();
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsXULElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|
{
|
|
if (NeedTooltipSupport(*this)) {
|
|
RemoveTooltipSupport();
|
|
}
|
|
|
|
// mControllers can own objects that are implemented
|
|
// in JavaScript (such as some implementations of
|
|
// nsIControllers. These objects prevent their global
|
|
// object's script object from being garbage collected,
|
|
// which means JS continues to hold an owning reference
|
|
// to the nsGlobalWindow, which owns the document,
|
|
// which owns this content. That's a cycle, so we break
|
|
// it here. (It might be better to break this by releasing
|
|
// mDocument in nsGlobalWindow::SetDocShell, but I'm not
|
|
// sure whether that would fix all possible cycles through
|
|
// mControllers.)
|
|
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
|
|
if (slots) {
|
|
slots->mControllers = nullptr;
|
|
}
|
|
|
|
nsStyledElement::UnbindFromTree(aDeep, aNullParent);
|
|
}
|
|
|
|
void
|
|
nsXULElement::UnregisterAccessKey(const nsAString& aOldValue)
|
|
{
|
|
// If someone changes the accesskey, unregister the old one
|
|
//
|
|
nsIDocument* doc = GetComposedDoc();
|
|
if (doc && !aOldValue.IsEmpty()) {
|
|
nsIPresShell *shell = doc->GetShell();
|
|
|
|
if (shell) {
|
|
Element* element = this;
|
|
|
|
// find out what type of content node this is
|
|
if (mNodeInfo->Equals(nsGkAtoms::label)) {
|
|
// For anonymous labels the unregistering must
|
|
// occur on the binding parent control.
|
|
// XXXldb: And what if the binding parent is null?
|
|
nsIContent* bindingParent = GetBindingParent();
|
|
element = bindingParent ? bindingParent->AsElement() : nullptr;
|
|
}
|
|
|
|
if (element) {
|
|
shell->GetPresContext()->EventStateManager()->
|
|
UnregisterAccessKey(element, aOldValue.First());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
|
const nsAttrValueOrString* aValue, bool aNotify)
|
|
{
|
|
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey &&
|
|
IsInUncomposedDoc()) {
|
|
nsAutoString oldValue;
|
|
if (GetAttr(aNamespaceID, aName, oldValue)) {
|
|
UnregisterAccessKey(oldValue);
|
|
}
|
|
} else if (aNamespaceID == kNameSpaceID_None &&
|
|
(aName == nsGkAtoms::command || aName == nsGkAtoms::observes) &&
|
|
IsInUncomposedDoc()) {
|
|
// XXX sXBL/XBL2 issue! Owner or current document?
|
|
nsAutoString oldValue;
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue);
|
|
if (oldValue.IsEmpty()) {
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::command, oldValue);
|
|
}
|
|
|
|
if (!oldValue.IsEmpty()) {
|
|
RemoveBroadcaster(oldValue);
|
|
}
|
|
} else if (aNamespaceID == kNameSpaceID_None &&
|
|
aValue &&
|
|
mNodeInfo->Equals(nsGkAtoms::window) &&
|
|
aName == nsGkAtoms::chromemargin) {
|
|
nsAttrValue attrValue;
|
|
// Make sure the margin format is valid first
|
|
if (!attrValue.ParseIntMarginValue(aValue->String())) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
} else if (aNamespaceID == kNameSpaceID_None &&
|
|
aName == nsGkAtoms::usercontextid) {
|
|
nsAutoString oldValue;
|
|
bool hasAttribute = GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, oldValue);
|
|
if (hasAttribute && (!aValue || !aValue->String().Equals(oldValue))) {
|
|
MOZ_ASSERT(false, "Changing usercontextid is not allowed.");
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
}
|
|
|
|
return nsStyledElement::BeforeSetAttr(aNamespaceID, aName,
|
|
aValue, aNotify);
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue,
|
|
nsIPrincipal* aSubjectPrincipal,
|
|
bool aNotify)
|
|
{
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
if (aValue) {
|
|
// Add popup and event listeners. We can't call AddListenerFor since
|
|
// the attribute isn't set yet.
|
|
MaybeAddPopupListener(aName);
|
|
if (nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL)) {
|
|
if (aValue->Type() == nsAttrValue::eString) {
|
|
SetEventHandler(aName, aValue->GetStringValue(), true);
|
|
} else {
|
|
nsAutoString body;
|
|
aValue->ToString(body);
|
|
SetEventHandler(aName, body, true);
|
|
}
|
|
}
|
|
|
|
nsIDocument* document = GetUncomposedDoc();
|
|
|
|
// Hide chrome if needed
|
|
if (mNodeInfo->Equals(nsGkAtoms::window)) {
|
|
if (aName == nsGkAtoms::hidechrome) {
|
|
HideWindowChrome(
|
|
aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
|
|
} else if (aName == nsGkAtoms::chromemargin) {
|
|
SetChromeMargins(aValue);
|
|
} else if (aName == nsGkAtoms::windowtype &&
|
|
document && document->GetRootElement() == this) {
|
|
MaybeUpdatePrivateLifetime();
|
|
}
|
|
}
|
|
// title and drawintitlebar are settable on
|
|
// any root node (windows, dialogs, etc)
|
|
if (document && document->GetRootElement() == this) {
|
|
if (aName == nsGkAtoms::title) {
|
|
document->NotifyPossibleTitleChange(false);
|
|
} else if (aName == nsGkAtoms::drawintitlebar) {
|
|
SetDrawsInTitlebar(
|
|
aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
|
|
} else if (aName == nsGkAtoms::drawtitle) {
|
|
SetDrawsTitle(
|
|
aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters));
|
|
} else if (aName == nsGkAtoms::localedir) {
|
|
// if the localedir changed on the root element, reset the document direction
|
|
if (document->IsXULDocument()) {
|
|
document->AsXULDocument()->ResetDocumentDirection();
|
|
}
|
|
} else if (aName == nsGkAtoms::lwtheme ||
|
|
aName == nsGkAtoms::lwthemetextcolor) {
|
|
// if the lwtheme changed, make sure to reset the document lwtheme cache
|
|
if (document->IsXULDocument()) {
|
|
document->AsXULDocument()->ResetDocumentLWTheme();
|
|
UpdateBrightTitlebarForeground(document);
|
|
}
|
|
} else if (aName == nsGkAtoms::brighttitlebarforeground) {
|
|
UpdateBrightTitlebarForeground(document);
|
|
}
|
|
}
|
|
} else {
|
|
if (mNodeInfo->Equals(nsGkAtoms::window)) {
|
|
if (aName == nsGkAtoms::hidechrome) {
|
|
HideWindowChrome(false);
|
|
} else if (aName == nsGkAtoms::chromemargin) {
|
|
ResetChromeMargins();
|
|
}
|
|
}
|
|
|
|
nsIDocument* doc = GetUncomposedDoc();
|
|
if (doc && doc->GetRootElement() == this) {
|
|
if (aName == nsGkAtoms::localedir) {
|
|
// if the localedir changed on the root element, reset the document direction
|
|
if (doc->IsXULDocument()) {
|
|
doc->AsXULDocument()->ResetDocumentDirection();
|
|
}
|
|
} else if ((aName == nsGkAtoms::lwtheme ||
|
|
aName == nsGkAtoms::lwthemetextcolor)) {
|
|
// if the lwtheme changed, make sure to restyle appropriately
|
|
if (doc->IsXULDocument()) {
|
|
doc->AsXULDocument()->ResetDocumentLWTheme();
|
|
UpdateBrightTitlebarForeground(doc);
|
|
}
|
|
} else if (aName == nsGkAtoms::brighttitlebarforeground) {
|
|
UpdateBrightTitlebarForeground(doc);
|
|
} else if (aName == nsGkAtoms::drawintitlebar) {
|
|
SetDrawsInTitlebar(false);
|
|
} else if (aName == nsGkAtoms::drawtitle) {
|
|
SetDrawsTitle(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aName == nsGkAtoms::tooltip || aName == nsGkAtoms::tooltiptext) {
|
|
if (!!aValue != !!aOldValue &&
|
|
IsInComposedDoc() &&
|
|
!NodeInfo()->Equals(nsGkAtoms::treechildren)) {
|
|
if (aValue) {
|
|
AddTooltipSupport();
|
|
} else {
|
|
RemoveTooltipSupport();
|
|
}
|
|
}
|
|
}
|
|
// XXX need to check if they're changing an event handler: if
|
|
// so, then we need to unhook the old one. Or something.
|
|
}
|
|
|
|
return nsStyledElement::AfterSetAttr(aNamespaceID, aName,
|
|
aValue, aOldValue, aSubjectPrincipal, aNotify);
|
|
}
|
|
|
|
void
|
|
nsXULElement::AddTooltipSupport()
|
|
{
|
|
nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
|
|
if (!listener) {
|
|
return;
|
|
}
|
|
|
|
listener->AddTooltipSupport(this);
|
|
}
|
|
|
|
void
|
|
nsXULElement::RemoveTooltipSupport()
|
|
{
|
|
nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
|
|
if (!listener) {
|
|
return;
|
|
}
|
|
|
|
listener->RemoveTooltipSupport(this);
|
|
}
|
|
|
|
bool
|
|
nsXULElement::ParseAttribute(int32_t aNamespaceID,
|
|
nsAtom* aAttribute,
|
|
const nsAString& aValue,
|
|
nsIPrincipal* aMaybeScriptedPrincipal,
|
|
nsAttrValue& aResult)
|
|
{
|
|
// Parse into a nsAttrValue
|
|
if (!nsStyledElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
|
|
aMaybeScriptedPrincipal, aResult)) {
|
|
// Fall back to parsing as atom for short values
|
|
aResult.ParseStringOrAtom(aValue);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId)
|
|
{
|
|
nsIDocument* doc = OwnerDoc();
|
|
if (!doc->IsXULDocument()) {
|
|
return;
|
|
}
|
|
if (Element* broadcaster = doc->GetElementById(broadcasterId)) {
|
|
doc->AsXULDocument()->RemoveBroadcastListenerFor(
|
|
*broadcaster, *this, NS_LITERAL_STRING("*"));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULElement::DestroyContent()
|
|
{
|
|
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
|
|
if (slots) {
|
|
slots->mControllers = nullptr;
|
|
}
|
|
|
|
nsStyledElement::DestroyContent();
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
nsXULElement::List(FILE* out, int32_t aIndent) const
|
|
{
|
|
nsCString prefix("XUL");
|
|
if (HasSlots()) {
|
|
prefix.Append('*');
|
|
}
|
|
prefix.Append(' ');
|
|
|
|
nsStyledElement::List(out, aIndent, prefix);
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage)
|
|
{
|
|
return (IsRootOfNativeAnonymousSubtree() &&
|
|
IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) &&
|
|
(aMessage == eMouseClick || aMessage == eMouseDoubleClick ||
|
|
aMessage == eXULCommand || aMessage == eContextMenu ||
|
|
aMessage == eDragStart || aMessage == eMouseAuxClick));
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor,
|
|
nsAutoString& aCommand)
|
|
{
|
|
// XXX sXBL/XBL2 issue! Owner or current document?
|
|
nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
|
|
NS_ENSURE_STATE(doc);
|
|
RefPtr<Element> commandElt = doc->GetElementById(aCommand);
|
|
if (commandElt) {
|
|
// Create a new command event to dispatch to the element
|
|
// pointed to by the command attribute. The new event's
|
|
// sourceEvent will be the original command event that we're
|
|
// handling.
|
|
RefPtr<Event> event = aVisitor.mDOMEvent;
|
|
uint16_t inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
|
|
while (event) {
|
|
NS_ENSURE_STATE(event->GetOriginalTarget() != commandElt);
|
|
RefPtr<XULCommandEvent> commandEvent = event->AsXULCommandEvent();
|
|
if (commandEvent) {
|
|
event = commandEvent->GetSourceEvent();
|
|
inputSource = commandEvent->InputSource();
|
|
} else {
|
|
event = nullptr;
|
|
}
|
|
}
|
|
WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent();
|
|
nsContentUtils::DispatchXULCommand(
|
|
commandElt,
|
|
orig->IsTrusted(),
|
|
aVisitor.mDOMEvent,
|
|
nullptr,
|
|
orig->IsControl(),
|
|
orig->IsAlt(),
|
|
orig->IsShift(),
|
|
orig->IsMeta(),
|
|
inputSource);
|
|
} else {
|
|
NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
|
|
{
|
|
aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119
|
|
if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) {
|
|
// Don't propagate these events from native anonymous scrollbar.
|
|
aVisitor.mCanHandle = true;
|
|
aVisitor.SetParentTarget(nullptr, false);
|
|
return;
|
|
}
|
|
if (aVisitor.mEvent->mMessage == eXULCommand &&
|
|
aVisitor.mEvent->mClass == eInputEventClass &&
|
|
aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this) &&
|
|
!IsXULElement(nsGkAtoms::command)) {
|
|
// Check that we really have an xul command event. That will be handled
|
|
// in a special way.
|
|
// See if we have a command elt. If so, we execute on the command
|
|
// instead of on our content element.
|
|
nsAutoString command;
|
|
if (aVisitor.mDOMEvent &&
|
|
aVisitor.mDOMEvent->AsXULCommandEvent() &&
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) &&
|
|
!command.IsEmpty()) {
|
|
// Stop building the event target chain for the original event.
|
|
// We don't want it to propagate to any DOM nodes.
|
|
aVisitor.mCanHandle = false;
|
|
aVisitor.mAutomaticChromeDispatch = false;
|
|
// Dispatch XUL command in PreHandleEvent to prevent it breaks event
|
|
// target chain creation
|
|
aVisitor.mWantsPreHandleEvent = true;
|
|
aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND;
|
|
return;
|
|
}
|
|
}
|
|
|
|
nsStyledElement::GetEventTargetParent(aVisitor);
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor)
|
|
{
|
|
if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) {
|
|
nsAutoString command;
|
|
GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
|
|
MOZ_ASSERT(!command.IsEmpty());
|
|
return DispatchXULCommand(aVisitor, command);
|
|
}
|
|
return nsStyledElement::PreHandleEvent(aVisitor);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Implementation methods
|
|
|
|
|
|
nsChangeHint
|
|
nsXULElement::GetAttributeChangeHint(const nsAtom* aAttribute,
|
|
int32_t aModType) const
|
|
{
|
|
nsChangeHint retval(nsChangeHint(0));
|
|
|
|
if (aAttribute == nsGkAtoms::value &&
|
|
(aModType == MutationEvent_Binding::REMOVAL ||
|
|
aModType == MutationEvent_Binding::ADDITION)) {
|
|
if (IsAnyOfXULElements(nsGkAtoms::label, nsGkAtoms::description))
|
|
// Label and description dynamically morph between a normal
|
|
// block and a cropping single-line XUL text frame. If the
|
|
// value attribute is being added or removed, then we need to
|
|
// return a hint of frame change. (See bugzilla bug 95475 for
|
|
// details.)
|
|
retval = nsChangeHint_ReconstructFrame;
|
|
} else {
|
|
// if left or top changes we reflow. This will happen in xul
|
|
// containers that manage positioned children such as a stack.
|
|
if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute ||
|
|
nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute ||
|
|
nsGkAtoms::start == aAttribute || nsGkAtoms::end == aAttribute)
|
|
retval = NS_STYLE_HINT_REFLOW;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
NS_IMETHODIMP_(bool)
|
|
nsXULElement::IsAttributeMapped(const nsAtom* aAttribute) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
nsIControllers*
|
|
nsXULElement::GetControllers(ErrorResult& rv)
|
|
{
|
|
if (! Controllers()) {
|
|
nsExtendedDOMSlots* slots = ExtendedDOMSlots();
|
|
|
|
rv = NS_NewXULControllers(nullptr, NS_GET_IID(nsIControllers),
|
|
reinterpret_cast<void**>(&slots->mControllers));
|
|
|
|
NS_ASSERTION(!rv.Failed(), "unable to create a controllers");
|
|
if (rv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return Controllers();
|
|
}
|
|
|
|
already_AddRefed<BoxObject>
|
|
nsXULElement::GetBoxObject(ErrorResult& rv)
|
|
{
|
|
// XXX sXBL/XBL2 issue! Owner or current document?
|
|
return OwnerDoc()->GetBoxObjectFor(this, rv);
|
|
}
|
|
|
|
void
|
|
nsXULElement::Click(CallerType aCallerType)
|
|
{
|
|
ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
|
|
aCallerType == CallerType::System);
|
|
}
|
|
|
|
void
|
|
nsXULElement::ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent)
|
|
{
|
|
if (BoolAttrIsTrue(nsGkAtoms::disabled))
|
|
return;
|
|
|
|
nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // Strong just in case
|
|
if (doc) {
|
|
RefPtr<nsPresContext> context = doc->GetPresContext();
|
|
if (context) {
|
|
// strong ref to PresContext so events don't destroy it
|
|
|
|
WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown,
|
|
nullptr, WidgetMouseEvent::eReal);
|
|
WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp,
|
|
nullptr, WidgetMouseEvent::eReal);
|
|
WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr,
|
|
WidgetMouseEvent::eReal);
|
|
eventDown.inputSource = eventUp.inputSource = eventClick.inputSource
|
|
= aInputSource;
|
|
|
|
// send mouse down
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
|
|
context, &eventDown, nullptr, &status);
|
|
|
|
// send mouse up
|
|
status = nsEventStatus_eIgnore; // reset status
|
|
EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
|
|
context, &eventUp, nullptr, &status);
|
|
|
|
// send mouse click
|
|
status = nsEventStatus_eIgnore; // reset status
|
|
EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
|
|
context, &eventClick, nullptr, &status);
|
|
|
|
// If the click has been prevented, lets skip the command call
|
|
// this is how a physical click works
|
|
if (status == nsEventStatus_eConsumeNoDefault) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// oncommand is fired when an element is clicked...
|
|
DoCommand();
|
|
}
|
|
|
|
void
|
|
nsXULElement::DoCommand()
|
|
{
|
|
nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // strong just in case
|
|
if (doc) {
|
|
nsContentUtils::DispatchXULCommand(this, true);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsXULElement::IsNodeOfType(uint32_t aFlags) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::AddPopupListener(nsAtom* aName)
|
|
{
|
|
// Add a popup listener to the element
|
|
bool isContext = (aName == nsGkAtoms::context ||
|
|
aName == nsGkAtoms::contextmenu);
|
|
uint32_t listenerFlag = isContext ?
|
|
XUL_ELEMENT_HAS_CONTENTMENU_LISTENER :
|
|
XUL_ELEMENT_HAS_POPUP_LISTENER;
|
|
|
|
if (HasFlag(listenerFlag)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMEventListener> listener =
|
|
new nsXULPopupListener(this, isContext);
|
|
|
|
// Add the popup as a listener on this element.
|
|
EventListenerManager* manager = GetOrCreateListenerManager();
|
|
SetFlags(listenerFlag);
|
|
|
|
if (isContext) {
|
|
manager->AddEventListenerByType(listener,
|
|
NS_LITERAL_STRING("contextmenu"),
|
|
TrustedEventsAtSystemGroupBubble());
|
|
} else {
|
|
manager->AddEventListenerByType(listener,
|
|
NS_LITERAL_STRING("mousedown"),
|
|
TrustedEventsAtSystemGroupBubble());
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
EventStates
|
|
nsXULElement::IntrinsicState() const
|
|
{
|
|
EventStates state = nsStyledElement::IntrinsicState();
|
|
|
|
if (IsReadWriteTextElement()) {
|
|
state |= NS_EVENT_STATE_MOZ_READWRITE;
|
|
state &= ~NS_EVENT_STATE_MOZ_READONLY;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype)
|
|
{
|
|
if (!aPrototype) {
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t i;
|
|
nsresult rv;
|
|
for (i = 0; i < aPrototype->mNumAttributes; ++i) {
|
|
nsXULPrototypeAttribute* protoattr = &aPrototype->mAttributes[i];
|
|
nsAttrValue attrValue;
|
|
|
|
// Style rules need to be cloned.
|
|
if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) {
|
|
DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue();
|
|
RefPtr<DeclarationBlock> declClone = decl->Clone();
|
|
|
|
nsString stringValue;
|
|
protoattr->mValue.ToString(stringValue);
|
|
|
|
attrValue.SetTo(declClone.forget(), &stringValue);
|
|
} else {
|
|
attrValue.SetTo(protoattr->mValue);
|
|
}
|
|
|
|
bool oldValueSet;
|
|
// XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName
|
|
if (protoattr->mName.IsAtom()) {
|
|
rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.Atom(),
|
|
attrValue, &oldValueSet);
|
|
} else {
|
|
rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.NodeInfo(),
|
|
attrValue, &oldValueSet);
|
|
}
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsXULElement::HideWindowChrome(bool aShouldHide)
|
|
{
|
|
nsIDocument* doc = GetUncomposedDoc();
|
|
if (!doc || doc->GetRootElement() != this)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
// only top level chrome documents can hide the window chrome
|
|
if (!doc->IsRootDisplayDocument())
|
|
return NS_OK;
|
|
|
|
nsPresContext* presContext = doc->GetPresContext();
|
|
|
|
if (presContext && presContext->IsChrome()) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
nsView* view = frame->GetClosestView();
|
|
|
|
if (view) {
|
|
nsIWidget* w = view->GetWidget();
|
|
NS_ENSURE_STATE(w);
|
|
w->HideWindowChrome(aShouldHide);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIWidget*
|
|
nsXULElement::GetWindowWidget()
|
|
{
|
|
nsIDocument* doc = GetComposedDoc();
|
|
|
|
// only top level chrome documents can set the titlebar color
|
|
if (doc && doc->IsRootDisplayDocument()) {
|
|
nsCOMPtr<nsISupports> container = doc->GetContainer();
|
|
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
|
|
if (baseWindow) {
|
|
nsCOMPtr<nsIWidget> mainWidget;
|
|
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
|
|
return mainWidget;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
class SetDrawInTitleBarEvent : public Runnable
|
|
{
|
|
public:
|
|
SetDrawInTitleBarEvent(nsIWidget* aWidget, bool aState)
|
|
: mozilla::Runnable("SetDrawInTitleBarEvent")
|
|
, mWidget(aWidget)
|
|
, mState(aState)
|
|
{}
|
|
|
|
NS_IMETHOD Run() override {
|
|
NS_ASSERTION(mWidget, "You shouldn't call this runnable with a null widget!");
|
|
|
|
mWidget->SetDrawsInTitlebar(mState);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsCOMPtr<nsIWidget> mWidget;
|
|
bool mState;
|
|
};
|
|
|
|
void
|
|
nsXULElement::SetDrawsInTitlebar(bool aState)
|
|
{
|
|
nsIWidget* mainWidget = GetWindowWidget();
|
|
if (mainWidget) {
|
|
nsContentUtils::AddScriptRunner(new SetDrawInTitleBarEvent(mainWidget, aState));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULElement::SetDrawsTitle(bool aState)
|
|
{
|
|
nsIWidget* mainWidget = GetWindowWidget();
|
|
if (mainWidget) {
|
|
// We can do this synchronously because SetDrawsTitle doesn't have any
|
|
// synchronous effects apart from a harmless invalidation.
|
|
mainWidget->SetDrawsTitle(aState);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULElement::UpdateBrightTitlebarForeground(nsIDocument* aDoc)
|
|
{
|
|
nsIWidget* mainWidget = GetWindowWidget();
|
|
if (mainWidget) {
|
|
// We can do this synchronously because SetBrightTitlebarForeground doesn't have any
|
|
// synchronous effects apart from a harmless invalidation.
|
|
mainWidget->SetUseBrightTitlebarForeground(
|
|
aDoc->GetDocumentLWTheme() == nsIDocument::Doc_Theme_Bright ||
|
|
aDoc->GetRootElement()->AttrValueIs(kNameSpaceID_None,
|
|
nsGkAtoms::brighttitlebarforeground,
|
|
NS_LITERAL_STRING("true"),
|
|
eCaseMatters));
|
|
}
|
|
}
|
|
|
|
class MarginSetter : public Runnable
|
|
{
|
|
public:
|
|
explicit MarginSetter(nsIWidget* aWidget)
|
|
: mozilla::Runnable("MarginSetter")
|
|
, mWidget(aWidget)
|
|
, mMargin(-1, -1, -1, -1)
|
|
{
|
|
}
|
|
MarginSetter(nsIWidget* aWidget, const LayoutDeviceIntMargin& aMargin)
|
|
: mozilla::Runnable("MarginSetter")
|
|
, mWidget(aWidget)
|
|
, mMargin(aMargin)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run() override
|
|
{
|
|
// SetNonClientMargins can dispatch native events, hence doing
|
|
// it off a script runner.
|
|
mWidget->SetNonClientMargins(mMargin);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
nsCOMPtr<nsIWidget> mWidget;
|
|
LayoutDeviceIntMargin mMargin;
|
|
};
|
|
|
|
void
|
|
nsXULElement::SetChromeMargins(const nsAttrValue* aValue)
|
|
{
|
|
if (!aValue)
|
|
return;
|
|
|
|
nsIWidget* mainWidget = GetWindowWidget();
|
|
if (!mainWidget)
|
|
return;
|
|
|
|
// top, right, bottom, left - see nsAttrValue
|
|
nsIntMargin margins;
|
|
bool gotMargins = false;
|
|
|
|
if (aValue->Type() == nsAttrValue::eIntMarginValue) {
|
|
gotMargins = aValue->GetIntMarginValue(margins);
|
|
} else {
|
|
nsAutoString tmp;
|
|
aValue->ToString(tmp);
|
|
gotMargins = nsContentUtils::ParseIntMarginValue(tmp, margins);
|
|
}
|
|
if (gotMargins) {
|
|
nsContentUtils::AddScriptRunner(
|
|
new MarginSetter(
|
|
mainWidget, LayoutDeviceIntMargin::FromUnknownMargin(margins)));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULElement::ResetChromeMargins()
|
|
{
|
|
nsIWidget* mainWidget = GetWindowWidget();
|
|
if (!mainWidget)
|
|
return;
|
|
// See nsIWidget
|
|
nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget));
|
|
}
|
|
|
|
bool
|
|
nsXULElement::BoolAttrIsTrue(nsAtom* aName) const
|
|
{
|
|
const nsAttrValue* attr =
|
|
GetAttrInfo(kNameSpaceID_None, aName).mValue;
|
|
|
|
return attr && attr->Type() == nsAttrValue::eAtom &&
|
|
attr->GetAtomValue() == nsGkAtoms::_true;
|
|
}
|
|
|
|
void
|
|
nsXULElement::RecompileScriptEventListeners()
|
|
{
|
|
int32_t i, count = mAttrsAndChildren.AttrCount();
|
|
for (i = 0; i < count; ++i) {
|
|
const nsAttrName *name = mAttrsAndChildren.AttrNameAt(i);
|
|
|
|
// Eventlistenener-attributes are always in the null namespace
|
|
if (!name->IsAtom()) {
|
|
continue;
|
|
}
|
|
|
|
nsAtom *attr = name->Atom();
|
|
if (!nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) {
|
|
continue;
|
|
}
|
|
|
|
nsAutoString value;
|
|
GetAttr(kNameSpaceID_None, attr, value);
|
|
SetEventHandler(attr, value, true);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsXULElement::IsEventAttributeNameInternal(nsAtom *aName)
|
|
{
|
|
return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL);
|
|
}
|
|
|
|
JSObject*
|
|
nsXULElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return dom::XULElement_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode)
|
|
if (tmp->mType == nsXULPrototypeNode::eType_Element) {
|
|
static_cast<nsXULPrototypeElement*>(tmp)->Unlink();
|
|
} else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
|
|
static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects();
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode)
|
|
if (tmp->mType == nsXULPrototypeNode::eType_Element) {
|
|
nsXULPrototypeElement *elem =
|
|
static_cast<nsXULPrototypeElement*>(tmp);
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo");
|
|
cb.NoteNativeChild(elem->mNodeInfo,
|
|
NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
|
|
uint32_t i;
|
|
for (i = 0; i < elem->mNumAttributes; ++i) {
|
|
const nsAttrName& name = elem->mAttributes[i].mName;
|
|
if (!name.IsAtom()) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
|
"mAttributes[i].mName.NodeInfo()");
|
|
cb.NoteNativeChild(name.NodeInfo(),
|
|
NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
|
|
}
|
|
}
|
|
ImplCycleCollectionTraverse(cb, elem->mChildren, "mChildren");
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode)
|
|
if (tmp->mType == nsXULPrototypeNode::eType_Script) {
|
|
nsXULPrototypeScript *script =
|
|
static_cast<nsXULPrototypeScript*>(tmp);
|
|
script->Trace(aCallbacks, aClosure);
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release)
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// nsXULPrototypeAttribute
|
|
//
|
|
|
|
nsXULPrototypeAttribute::~nsXULPrototypeAttribute()
|
|
{
|
|
MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// nsXULPrototypeElement
|
|
//
|
|
|
|
nsresult
|
|
nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Write basic prototype data
|
|
rv = aStream->Write32(mType);
|
|
|
|
// Write Node Info
|
|
int32_t index = aNodeInfos->IndexOf(mNodeInfo);
|
|
NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
|
|
nsresult tmp = aStream->Write32(index);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
// Write Attributes
|
|
tmp = aStream->Write32(mNumAttributes);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
nsAutoString attributeValue;
|
|
uint32_t i;
|
|
for (i = 0; i < mNumAttributes; ++i) {
|
|
RefPtr<mozilla::dom::NodeInfo> ni;
|
|
if (mAttributes[i].mName.IsAtom()) {
|
|
ni = mNodeInfo->NodeInfoManager()->
|
|
GetNodeInfo(mAttributes[i].mName.Atom(), nullptr,
|
|
kNameSpaceID_None, nsINode::ATTRIBUTE_NODE);
|
|
NS_ASSERTION(ni, "the nodeinfo should already exist");
|
|
} else {
|
|
ni = mAttributes[i].mName.NodeInfo();
|
|
}
|
|
|
|
index = aNodeInfos->IndexOf(ni);
|
|
NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index");
|
|
tmp = aStream->Write32(index);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
mAttributes[i].mValue.ToString(attributeValue);
|
|
tmp = aStream->WriteWStringZ(attributeValue.get());
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
}
|
|
|
|
// Now write children
|
|
tmp = aStream->Write32(uint32_t(mChildren.Length()));
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
for (i = 0; i < mChildren.Length(); i++) {
|
|
nsXULPrototypeNode* child = mChildren[i].get();
|
|
switch (child->mType) {
|
|
case eType_Element:
|
|
case eType_Text:
|
|
case eType_PI:
|
|
tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
break;
|
|
case eType_Script:
|
|
tmp = aStream->Write32(child->mType);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(child);
|
|
|
|
tmp = aStream->Write8(script->mOutOfLine);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
if (! script->mOutOfLine) {
|
|
tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
} else {
|
|
tmp = aStream->WriteCompoundObject(script->mSrcURI,
|
|
NS_GET_IID(nsIURI),
|
|
true);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
if (script->HasScriptObject()) {
|
|
// This may return NS_OK without muxing script->mSrcURI's
|
|
// data into the cache file, in the case where that
|
|
// muxed document is already there (written by a prior
|
|
// session, or by an earlier cache episode during this
|
|
// session).
|
|
tmp = script->SerializeOutOfLine(aStream, aProtoDoc);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
nsIURI* aDocumentURI,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
MOZ_ASSERT(aNodeInfos, "missing nodeinfo array");
|
|
|
|
// Read Node Info
|
|
uint32_t number = 0;
|
|
nsresult rv = aStream->Read32(&number);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr);
|
|
if (!mNodeInfo) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// Read Attributes
|
|
rv = aStream->Read32(&number);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
mNumAttributes = int32_t(number);
|
|
|
|
if (mNumAttributes > 0) {
|
|
mAttributes = new (fallible) nsXULPrototypeAttribute[mNumAttributes];
|
|
if (!mAttributes) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
nsAutoString attributeValue;
|
|
for (uint32_t i = 0; i < mNumAttributes; ++i) {
|
|
rv = aStream->Read32(&number);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr);
|
|
if (!ni) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
mAttributes[i].mName.SetTo(ni);
|
|
|
|
rv = aStream->ReadString(attributeValue);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
rv = SetAttrAt(i, attributeValue, aDocumentURI);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
}
|
|
}
|
|
|
|
rv = aStream->Read32(&number);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
uint32_t numChildren = int32_t(number);
|
|
|
|
if (numChildren > 0) {
|
|
if (!mChildren.SetCapacity(numChildren, fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < numChildren; i++) {
|
|
rv = aStream->Read32(&number);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
Type childType = (Type)number;
|
|
|
|
RefPtr<nsXULPrototypeNode> child;
|
|
|
|
switch (childType) {
|
|
case eType_Element:
|
|
child = new nsXULPrototypeElement();
|
|
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
|
|
aNodeInfos);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
break;
|
|
case eType_Text:
|
|
child = new nsXULPrototypeText();
|
|
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
|
|
aNodeInfos);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
break;
|
|
case eType_PI:
|
|
child = new nsXULPrototypePI();
|
|
rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI,
|
|
aNodeInfos);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
break;
|
|
case eType_Script: {
|
|
// language version/options obtained during deserialization.
|
|
RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0);
|
|
|
|
rv = aStream->ReadBoolean(&script->mOutOfLine);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
if (!script->mOutOfLine) {
|
|
rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI,
|
|
aNodeInfos);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
} else {
|
|
nsCOMPtr<nsISupports> supports;
|
|
rv = aStream->ReadObject(true, getter_AddRefs(supports));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
script->mSrcURI = do_QueryInterface(supports);
|
|
|
|
rv = script->DeserializeOutOfLine(aStream, aProtoDoc);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) return rv;
|
|
}
|
|
|
|
child = script.forget();
|
|
break;
|
|
}
|
|
default:
|
|
MOZ_ASSERT(false, "Unexpected child type!");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
MOZ_ASSERT(child, "Don't append null to mChildren");
|
|
MOZ_ASSERT(child->mType == childType);
|
|
mChildren.AppendElement(child);
|
|
|
|
// Oh dear. Something failed during the deserialization.
|
|
// We don't know what. But likely consequences of failed
|
|
// deserializations included calls to |AbortCaching| which
|
|
// shuts down the cache and closes our streams.
|
|
// If that happens, next time through this loop, we die a messy
|
|
// death. So, let's just fail now, and propagate that failure
|
|
// upward so that the ChromeProtocolHandler knows it can't use
|
|
// a cached chrome channel for this.
|
|
if (NS_WARN_IF(NS_FAILED(rv)))
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
|
|
nsIURI* aDocumentURI)
|
|
{
|
|
MOZ_ASSERT(aPos < mNumAttributes, "out-of-bounds");
|
|
|
|
// WARNING!!
|
|
// This code is largely duplicated in nsXULElement::SetAttr.
|
|
// Any changes should be made to both functions.
|
|
|
|
if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
|
|
mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mAttributes[aPos].mName.Equals(nsGkAtoms::id) &&
|
|
!aValue.IsEmpty()) {
|
|
mHasIdAttribute = true;
|
|
// Store id as atom.
|
|
// id="" means that the element has no id. Not that it has
|
|
// emptystring as id.
|
|
mAttributes[aPos].mValue.ParseAtom(aValue);
|
|
|
|
return NS_OK;
|
|
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
|
|
// Store is as atom.
|
|
mAttributes[aPos].mValue.ParseAtom(aValue);
|
|
mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
|
|
|
|
return NS_OK;
|
|
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) {
|
|
mHasClassAttribute = true;
|
|
// Compute the element's class list
|
|
mAttributes[aPos].mValue.ParseAtomArray(aValue);
|
|
|
|
return NS_OK;
|
|
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) {
|
|
mHasStyleAttribute = true;
|
|
// Parse the element's 'style' attribute
|
|
|
|
// This is basically duplicating what nsINode::NodePrincipal() does
|
|
nsIPrincipal* principal =
|
|
mNodeInfo->NodeInfoManager()->DocumentPrincipal();
|
|
// XXX Get correct Base URI (need GetBaseURI on *prototype* element)
|
|
// TODO: If we implement Content Security Policy for chrome documents
|
|
// as has been discussed, the CSP should be checked here to see if
|
|
// inline styles are allowed to be applied.
|
|
RefPtr<URLExtraData> data =
|
|
new URLExtraData(aDocumentURI, aDocumentURI, principal);
|
|
RefPtr<DeclarationBlock> declaration =
|
|
DeclarationBlock::FromCssText(
|
|
aValue, data, eCompatibility_FullStandards, nullptr);
|
|
if (declaration) {
|
|
mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
// Don't abort if parsing failed, it could just be malformed css.
|
|
}
|
|
|
|
mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsXULPrototypeElement::Unlink()
|
|
{
|
|
mNumAttributes = 0;
|
|
delete[] mAttributes;
|
|
mAttributes = nullptr;
|
|
mChildren.Clear();
|
|
}
|
|
|
|
void
|
|
nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc)
|
|
{
|
|
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
|
|
nsXULPrototypeNode* child = mChildren[i];
|
|
if (child->mType == nsXULPrototypeNode::eType_Element) {
|
|
static_cast<nsXULPrototypeElement*>(child)->TraceAllScripts(aTrc);
|
|
} else if (child->mType == nsXULPrototypeNode::eType_Script) {
|
|
static_cast<nsXULPrototypeScript*>(child)->TraceScriptObject(aTrc);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// nsXULPrototypeScript
|
|
//
|
|
|
|
nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo)
|
|
: nsXULPrototypeNode(eType_Script),
|
|
mLineNo(aLineNo),
|
|
mSrcLoading(false),
|
|
mOutOfLine(true),
|
|
mSrcLoadWaiters(nullptr),
|
|
mScriptObject(nullptr)
|
|
{
|
|
}
|
|
|
|
|
|
nsXULPrototypeScript::~nsXULPrototypeScript()
|
|
{
|
|
UnlinkJSObjects();
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED);
|
|
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(xpc::CompilationScope())) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
|
|
!mScriptObject,
|
|
"script source still loading when serializing?!");
|
|
if (!mScriptObject)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Write basic prototype data
|
|
nsresult rv;
|
|
rv = aStream->Write32(mLineNo);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = aStream->Write32(0); // See bug 1418294.
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
JSContext* cx = jsapi.cx();
|
|
JS::Rooted<JSScript*> script(cx, mScriptObject);
|
|
MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
|
|
return nsContentUtils::XPConnect()->WriteScript(aStream, cx, script);
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc)
|
|
{
|
|
nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
bool isChrome = false;
|
|
if (NS_FAILED(mSrcURI->SchemeIs("chrome", &isChrome)) || !isChrome)
|
|
// Don't cache scripts that don't come from chrome uris.
|
|
return rv;
|
|
|
|
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
|
if (!cache)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ASSERTION(cache->IsEnabled(),
|
|
"writing to the cache file, but the XUL cache is off?");
|
|
bool exists;
|
|
cache->HasData(mSrcURI, &exists);
|
|
|
|
/* return will be NS_OK from GetAsciiSpec.
|
|
* that makes no sense.
|
|
* nor does returning NS_OK from HasMuxedDocument.
|
|
* XXX return something meaningful.
|
|
*/
|
|
if (exists)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIObjectOutputStream> oos;
|
|
rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsresult tmp = Serialize(oos, aProtoDoc, nullptr);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
tmp = cache->FinishOutputStream(mSrcURI);
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
if (NS_FAILED(rv))
|
|
cache->AbortCaching();
|
|
return rv;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
nsIURI* aDocumentURI,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
nsresult rv;
|
|
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr ||
|
|
!mScriptObject,
|
|
"prototype script not well-initialized when deserializing?!");
|
|
|
|
// Read basic prototype data
|
|
rv = aStream->Read32(&mLineNo);
|
|
if (NS_FAILED(rv)) return rv;
|
|
uint32_t dummy;
|
|
rv = aStream->Read32(&dummy); // See bug 1418294.
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(xpc::CompilationScope())) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
JS::Rooted<JSScript*> newScriptObject(cx);
|
|
rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx,
|
|
newScriptObject.address());
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
Set(newScriptObject);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
|
|
nsXULPrototypeDocument* aProtoDoc)
|
|
{
|
|
// Keep track of failure via rv, so we can
|
|
// AbortCaching if things look bad.
|
|
nsresult rv = NS_OK;
|
|
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
|
|
|
nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
|
|
if (cache) {
|
|
bool useXULCache = true;
|
|
if (mSrcURI) {
|
|
// NB: we must check the XUL script cache early, to avoid
|
|
// multiple deserialization attempts for a given script.
|
|
// Note that XULDocument::LoadScript
|
|
// checks the XUL script cache too, in order to handle the
|
|
// serialization case.
|
|
//
|
|
// We need do this only for <script src='strres.js'> and the
|
|
// like, i.e., out-of-line scripts that are included by several
|
|
// different XUL documents stored in the cache file.
|
|
useXULCache = cache->IsEnabled();
|
|
|
|
if (useXULCache) {
|
|
JSScript* newScriptObject =
|
|
cache->GetScript(mSrcURI);
|
|
if (newScriptObject)
|
|
Set(newScriptObject);
|
|
}
|
|
}
|
|
|
|
if (!mScriptObject) {
|
|
if (mSrcURI) {
|
|
rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput));
|
|
}
|
|
// If !mSrcURI, we have an inline script. We shouldn't have
|
|
// to do anything else in that case, I think.
|
|
|
|
// We do reflect errors into rv, but our caller may want to
|
|
// ignore our return value, because mScriptObject will be null
|
|
// after any error, and that suffices to cause the script to
|
|
// be reloaded (from the src= URI, if any) and recompiled.
|
|
// We're better off slow-loading than bailing out due to a
|
|
// error.
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = Deserialize(objectInput, aProtoDoc, nullptr, nullptr);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
if (useXULCache && mSrcURI) {
|
|
bool isChrome = false;
|
|
mSrcURI->SchemeIs("chrome", &isChrome);
|
|
if (isChrome) {
|
|
JS::Rooted<JSScript*> script(RootingCx(), GetScriptObject());
|
|
cache->PutScript(mSrcURI, script);
|
|
}
|
|
}
|
|
cache->FinishInputStream(mSrcURI);
|
|
} else {
|
|
// If mSrcURI is not in the cache,
|
|
// rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
|
|
// update the cache file to hold a serialization of
|
|
// this script, once it has finished loading.
|
|
if (rv != NS_ERROR_NOT_AVAILABLE)
|
|
cache->AbortCaching();
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
class NotifyOffThreadScriptCompletedRunnable : public Runnable
|
|
{
|
|
// An array of all outstanding script receivers. All reference counting of
|
|
// these objects happens on the main thread. When we return to the main
|
|
// thread from script compilation we make sure our receiver is still in
|
|
// this array (still alive) before proceeding. This array is cleared during
|
|
// shutdown, potentially before all outstanding script compilations have
|
|
// finished. We do not need to worry about pointer replay here, because
|
|
// a) we should not be starting script compilation after clearing this
|
|
// array and b) in all other cases the receiver will still be alive.
|
|
static StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> sReceivers;
|
|
static bool sSetupClearOnShutdown;
|
|
|
|
nsIOffThreadScriptReceiver* mReceiver;
|
|
JS::OffThreadToken* mToken;
|
|
|
|
public:
|
|
NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver* aReceiver,
|
|
JS::OffThreadToken* aToken)
|
|
: mozilla::Runnable("NotifyOffThreadScriptCompletedRunnable")
|
|
, mReceiver(aReceiver)
|
|
, mToken(aToken)
|
|
{
|
|
}
|
|
|
|
static void NoteReceiver(nsIOffThreadScriptReceiver* aReceiver)
|
|
{
|
|
if (!sSetupClearOnShutdown) {
|
|
ClearOnShutdown(&sReceivers);
|
|
sSetupClearOnShutdown = true;
|
|
sReceivers = new nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>();
|
|
}
|
|
|
|
// If we ever crash here, it's because we tried to lazy compile script
|
|
// too late in shutdown.
|
|
sReceivers->AppendElement(aReceiver);
|
|
}
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
};
|
|
|
|
StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> NotifyOffThreadScriptCompletedRunnable::sReceivers;
|
|
bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown = false;
|
|
|
|
NS_IMETHODIMP
|
|
NotifyOffThreadScriptCompletedRunnable::Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
JS::Rooted<JSScript*> script(RootingCx());
|
|
{
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(xpc::CompilationScope())) {
|
|
// Now what? I guess we just leak... this should probably never
|
|
// happen.
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
script = JS::FinishOffThreadScript(cx, mToken);
|
|
}
|
|
|
|
if (!sReceivers) {
|
|
// We've already shut down.
|
|
return NS_OK;
|
|
}
|
|
|
|
auto index = sReceivers->IndexOf(mReceiver);
|
|
MOZ_RELEASE_ASSERT(index != sReceivers->NoIndex);
|
|
nsCOMPtr<nsIOffThreadScriptReceiver> receiver = (*sReceivers)[index].forget();
|
|
sReceivers->RemoveElementAt(index);
|
|
|
|
return receiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
|
|
}
|
|
|
|
static void
|
|
OffThreadScriptReceiverCallback(JS::OffThreadToken* aToken, void* aCallbackData)
|
|
{
|
|
// Be careful not to adjust the refcount on the receiver, as this callback
|
|
// may be invoked off the main thread.
|
|
nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData);
|
|
RefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
|
|
new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken);
|
|
NS_DispatchToMainThread(notify);
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
|
|
nsIURI* aURI, uint32_t aLineNo,
|
|
nsIDocument* aDocument,
|
|
nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
|
|
{
|
|
// We'll compile the script in the compilation scope.
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(xpc::CompilationScope())) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
nsresult rv;
|
|
nsAutoCString urlspec;
|
|
nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec, &rv);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
// Ok, compile it to create a prototype script object!
|
|
JS::CompileOptions options(cx);
|
|
options.setIntroductionType("scriptElement")
|
|
.setFileAndLine(urlspec.get(), aLineNo);
|
|
// If the script was inline, tell the JS parser to save source for
|
|
// Function.prototype.toSource(). If it's out of line, we retrieve the
|
|
// source from the files on demand.
|
|
options.setSourceIsLazy(mOutOfLine);
|
|
JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
|
|
if (scope) {
|
|
JS::ExposeObjectToActiveJS(scope);
|
|
}
|
|
|
|
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
|
|
if (!JS::CompileOffThread(cx, options,
|
|
aSrcBuf.get(), aSrcBuf.length(),
|
|
OffThreadScriptReceiverCallback,
|
|
static_cast<void*>(aOffThreadReceiver))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
|
|
} else {
|
|
JS::Rooted<JSScript*> script(cx);
|
|
if (!JS::Compile(cx, options, aSrcBuf, &script))
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
Set(script);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeScript::Compile(const char16_t* aText,
|
|
int32_t aTextLength,
|
|
nsIURI* aURI,
|
|
uint32_t aLineNo,
|
|
nsIDocument* aDocument,
|
|
nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
|
|
{
|
|
JS::SourceBufferHolder srcBuf(aText, aTextLength,
|
|
JS::SourceBufferHolder::NoOwnership);
|
|
return Compile(srcBuf, aURI, aLineNo, aDocument, aOffThreadReceiver);
|
|
}
|
|
|
|
void
|
|
nsXULPrototypeScript::UnlinkJSObjects()
|
|
{
|
|
if (mScriptObject) {
|
|
mScriptObject = nullptr;
|
|
mozilla::DropJSObjects(this);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsXULPrototypeScript::Set(JSScript* aObject)
|
|
{
|
|
MOZ_ASSERT(!mScriptObject, "Leaking script object.");
|
|
if (!aObject) {
|
|
mScriptObject = nullptr;
|
|
return;
|
|
}
|
|
|
|
mScriptObject = aObject;
|
|
mozilla::HoldJSObjects(this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// nsXULPrototypeText
|
|
//
|
|
|
|
nsresult
|
|
nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Write basic prototype data
|
|
rv = aStream->Write32(mType);
|
|
|
|
nsresult tmp = aStream->WriteWStringZ(mValue.get());
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
nsIURI* aDocumentURI,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
nsresult rv = aStream->ReadString(mValue);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// nsXULPrototypePI
|
|
//
|
|
|
|
nsresult
|
|
nsXULPrototypePI::Serialize(nsIObjectOutputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Write basic prototype data
|
|
rv = aStream->Write32(mType);
|
|
|
|
nsresult tmp = aStream->WriteWStringZ(mTarget.get());
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
tmp = aStream->WriteWStringZ(mData.get());
|
|
if (NS_FAILED(tmp)) {
|
|
rv = tmp;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream,
|
|
nsXULPrototypeDocument* aProtoDoc,
|
|
nsIURI* aDocumentURI,
|
|
const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos)
|
|
{
|
|
nsresult rv;
|
|
|
|
rv = aStream->ReadString(mTarget);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = aStream->ReadString(mData);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return rv;
|
|
}
|