[XForms] Events thrown in model need to be deferred. Bug 315712, r=doronr+allan

This commit is contained in:
aaronr%us.ibm.com 2006-05-17 19:30:36 +00:00
Родитель 79a279ebe9
Коммит bb7c0c3d27
8 изменённых файлов: 323 добавлений и 67 удалений

Просмотреть файл

@ -170,4 +170,10 @@ interface nsIModelElementPrivate : nsIXFormsModelElement
* have loaded their data.
*/
void messageLoadFinished();
/**
* Returns true when the model has been notified that the DOMContentLoaded
* event has been fired on the XForms document.
*/
readonly attribute boolean hasDOMContentFired;
};

Просмотреть файл

@ -65,6 +65,7 @@ nsIAtom *nsXFormsAtoms::fatalError;
nsIAtom *nsXFormsAtoms::isInstanceDocument;
nsIAtom *nsXFormsAtoms::instanceDocumentOwner;
nsIAtom *nsXFormsAtoms::externalMessagesProperty;
nsIAtom* nsXFormsAtoms::deferredEventListProperty;
const nsStaticAtom nsXFormsAtoms::Atoms_info[] = {
{ "src", &nsXFormsAtoms::src },
@ -92,7 +93,8 @@ const nsStaticAtom nsXFormsAtoms::Atoms_info[] = {
{ "fatalError", &nsXFormsAtoms::fatalError },
{ "isInstanceDocument", &nsXFormsAtoms::isInstanceDocument },
{ "instanceDocumentOwner", &nsXFormsAtoms::instanceDocumentOwner },
{ "ExternalMessagesProperty", &nsXFormsAtoms::externalMessagesProperty }
{ "ExternalMessagesProperty", &nsXFormsAtoms::externalMessagesProperty },
{ "DeferredEventListProperty",&nsXFormsAtoms::deferredEventListProperty }
};
void

Просмотреть файл

@ -73,6 +73,7 @@ class nsXFormsAtoms
static NS_HIDDEN_(nsIAtom *) isInstanceDocument;
static NS_HIDDEN_(nsIAtom *) instanceDocumentOwner;
static NS_HIDDEN_(nsIAtom *) externalMessagesProperty;
static NS_HIDDEN_(nsIAtom *) deferredEventListProperty;
static NS_HIDDEN_(void) InitAtoms();

Просмотреть файл

@ -514,8 +514,9 @@ nsXFormsControlStubBase::HandleDefault(nsIDOMEvent *aEvent,
focusController->MoveFocus(PR_FALSE, nsnull);
}
} else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_BindingException].name)) {
*aHandled = nsXFormsUtils::HandleFatalError(mElement,
NS_LITERAL_STRING("XFormsBindingException"));
// we threw up a popup during the nsXFormsUtils::DispatchEvent that sent
// this error to this control
*aHandled = PR_TRUE;
}
}

Просмотреть файл

@ -242,7 +242,8 @@ nsXFormsLabelElement::LoadExternalLabel(const nsAString& aSrc)
nsCOMPtr<nsIModelElementPrivate> modelPriv =
nsXFormsUtils::GetModel(mElement);
nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
nsXFormsUtils::DispatchEvent(model, eEvent_LinkError);
nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull,
mElement);
}
}
} else {
@ -388,7 +389,7 @@ nsXFormsLabelElement::OnStopRequest(nsIRequest *aRequest,
nsCOMPtr<nsIModelElementPrivate> modelPriv =
nsXFormsUtils::GetModel(mElement);
nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
nsXFormsUtils::DispatchEvent(model, eEvent_LinkError);
nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement);
mSrcAttrText.Truncate();
}

Просмотреть файл

@ -842,12 +842,7 @@ nsXFormsModelElement::InitializeInstances()
// this is a fatal error
nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement);
nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
rv = NS_OK;
if (!nsXFormsUtils::HandleFatalError(mElement,
NS_LITERAL_STRING("XFormsLinkException"))) {
rv = NS_ERROR_FAILURE;
}
return rv;
return NS_OK;
}
}
}
@ -944,8 +939,9 @@ nsXFormsModelElement::HandleDefault(nsIDOMEvent *aEvent, PRBool *aHandled)
} else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Reset].name)) {
Reset();
} else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_BindingException].name)) {
*aHandled = nsXFormsUtils::HandleFatalError(mElement,
NS_LITERAL_STRING("XFormsBindingException"));
// we threw up a popup during the nsXFormsUtils::DispatchEvent that sent
// this error to the model
*aHandled = PR_TRUE;
} else {
*aHandled = PR_FALSE;
}
@ -1369,8 +1365,6 @@ nsXFormsModelElement::OnError(nsresult aStatus,
{
nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement);
nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
nsXFormsUtils::HandleFatalError(mElement,
NS_LITERAL_STRING("XFormsLinkException"));
return NS_OK;
}
@ -1483,8 +1477,6 @@ nsXFormsModelElement::InstanceLoadFinished(PRBool aSuccess)
// finish construction, which is wrong.
nsXFormsUtils::ReportError(NS_LITERAL_STRING("instanceLoadError"), mElement);
nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
nsXFormsUtils::HandleFatalError(mElement,
NS_LITERAL_STRING("XFormsLinkException"));
return NS_OK;
}
@ -2073,8 +2065,6 @@ nsXFormsModelElement::MaybeNotifyCompletion()
nsXFormsUtils::ReportError(NS_LITERAL_STRING("invalidExtFunction"),
tElement);
nsXFormsUtils::DispatchEvent(tElement, eEvent_ComputeException);
nsXFormsUtils::HandleFatalError(tElement,
NS_LITERAL_STRING("XFormsComputeException"));
return;
}
}
@ -2417,6 +2407,16 @@ nsXFormsModelElement::MessageLoadFinished()
return NS_OK;
}
NS_IMETHODIMP
nsXFormsModelElement::GetHasDOMContentFired(PRBool *aLoaded)
{
NS_ENSURE_ARG_POINTER(aLoaded);
*aLoaded = mDocumentLoaded;
return NS_OK;
}
/* static */ void
nsXFormsModelElement::Startup()
{
@ -2536,6 +2536,11 @@ nsXFormsModelElement::HandleLoad(nsIDOMEvent* aEvent)
mDocumentLoaded = PR_TRUE;
nsCOMPtr<nsIDOMDocument> document;
mElement->GetOwnerDocument(getter_AddRefs(document));
NS_ENSURE_STATE(document);
nsXFormsUtils::DispatchDeferredEvents(document);
// dispatch xforms-model-construct, xforms-rebuild, xforms-recalculate,
// xforms-revalidate
@ -2568,8 +2573,6 @@ nsXFormsModelElement::HandleLoad(nsIDOMEvent* aEvent)
// this is a fatal error
nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement);
nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
nsXFormsUtils::HandleFatalError(mElement,
NS_LITERAL_STRING("XFormsLinkException"));
return NS_OK;
}
}

Просмотреть файл

@ -98,6 +98,7 @@
#include "nsIDOMEntity.h"
#include "nsIDOMNotation.h"
#include "nsIEventStateManager.h"
#include "nsXFormsModelElement.h"
#define CANCELABLE 0x01
#define BUBBLES 0x02
@ -195,6 +196,13 @@ const PRInt32 kDisabledIntrinsicState =
NS_EVENT_STATE_OPTIONAL |
NS_EVENT_STATE_MOZ_READWRITE;
struct EventItem
{
nsXFormsEvent event;
nsCOMPtr<nsIDOMNode> eventTarget;
nsCOMPtr<nsIDOMElement> srcElement;
};
/* static */ nsresult
nsXFormsUtils::Init()
{
@ -485,7 +493,7 @@ nsXFormsUtils::EvaluateXPath(const nsAString &aExpression,
nsCOMPtr<nsIDOMElement> resolverElement = do_QueryInterface(aResolverNode);
nsCOMPtr<nsIModelElementPrivate> modelPriv = nsXFormsUtils::GetModel(resolverElement);
nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
DispatchEvent(model, eEvent_ComputeException);
DispatchEvent(model, eEvent_ComputeException, nsnull, resolverElement);
}
return rv;
@ -893,51 +901,12 @@ nsXFormsUtils::SetSingleNodeBindingValue(nsIDOMElement *aElement,
return PR_FALSE;
}
/* static */ nsresult
nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
PRBool *aDefaultActionEnabled)
nsresult
DispatchXFormsEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
PRBool *aDefaultActionEnabled)
{
if (!aTarget)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIXFormsControl> control = do_QueryInterface(aTarget);
if (control) {
switch (aEvent) {
case eEvent_Previous:
case eEvent_Next:
case eEvent_Focus:
case eEvent_Help:
case eEvent_Hint:
case eEvent_DOMActivate:
case eEvent_ValueChanged:
case eEvent_Valid:
case eEvent_Invalid:
case eEvent_DOMFocusIn:
case eEvent_DOMFocusOut:
case eEvent_Readonly:
case eEvent_Readwrite:
case eEvent_Required:
case eEvent_Optional:
case eEvent_Enabled:
case eEvent_Disabled:
case eEvent_InRange:
case eEvent_OutOfRange:
{
PRBool acceptableEventTarget = PR_FALSE;
control->IsEventTarget(&acceptableEventTarget);
if (!acceptableEventTarget) {
return NS_OK;
}
break;
}
default:
break;
}
}
nsCOMPtr<nsIDOMDocument> domDoc;
aTarget->GetOwnerDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDOMDocumentEvent> doc = do_QueryInterface(domDoc);
NS_ENSURE_STATE(doc);
@ -952,7 +921,7 @@ nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aTarget);
NS_ENSURE_STATE(target);
SetEventTrusted(event, aTarget);
nsXFormsUtils::SetEventTrusted(event, aTarget);
PRBool defaultActionEnabled = PR_TRUE;
nsresult rv = target->DispatchEvent(event, &defaultActionEnabled);
@ -960,9 +929,274 @@ nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
if (NS_SUCCEEDED(rv) && aDefaultActionEnabled)
*aDefaultActionEnabled = defaultActionEnabled;
// if this is a fatal error, then display the fatal error dialog if desired
switch (aEvent) {
case eEvent_LinkException:
{
nsCOMPtr<nsIDOMElement> targetEle(do_QueryInterface(aTarget));
nsXFormsUtils::HandleFatalError(targetEle,
NS_LITERAL_STRING("XFormsLinkException"));
break;
}
case eEvent_ComputeException:
{
nsCOMPtr<nsIDOMElement> targetEle(do_QueryInterface(aTarget));
nsXFormsUtils::HandleFatalError(targetEle,
NS_LITERAL_STRING("XFormsComputeException"));
break;
}
case eEvent_BindingException:
{
nsCOMPtr<nsIDOMElement> targetEle(do_QueryInterface(aTarget));
nsXFormsUtils::HandleFatalError(targetEle,
NS_LITERAL_STRING("XFormsBindingException"));
break;
}
default:
break;
}
return rv;
}
static void
DeleteVoidArray(void *aObject,
nsIAtom *aPropertyName,
void *aPropertyValue,
void *aData)
{
nsVoidArray *array = NS_STATIC_CAST(nsVoidArray *, aPropertyValue);
PRInt32 count = array->Count();
for (PRInt32 i = 0; i < count; i++) {
EventItem *item = (EventItem *)array->ElementAt(i);
delete item;
}
array->Clear();
delete array;
}
nsresult
DeferDispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
nsIDOMElement *aSrcElement)
{
nsCOMPtr<nsIDOMDocument> domDoc;
if (aTarget) {
aTarget->GetOwnerDocument(getter_AddRefs(domDoc));
} else {
if (aSrcElement) {
aSrcElement->GetOwnerDocument(getter_AddRefs(domDoc));
}
}
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
NS_ENSURE_STATE(doc);
nsVoidArray *eventList =
NS_STATIC_CAST(nsVoidArray *,
doc->GetProperty(nsXFormsAtoms::deferredEventListProperty));
if (!eventList) {
eventList = new nsVoidArray(16);
if (!eventList)
return NS_ERROR_OUT_OF_MEMORY;
doc->SetProperty(nsXFormsAtoms::deferredEventListProperty, eventList,
DeleteVoidArray);
}
EventItem *deferredEvent = new EventItem;
NS_ENSURE_TRUE(deferredEvent, NS_ERROR_OUT_OF_MEMORY);
deferredEvent->event = aEvent;
deferredEvent->eventTarget = aTarget;
deferredEvent->srcElement = aSrcElement;
eventList->AppendElement(deferredEvent);
return NS_OK;
}
/* static */ nsresult
nsXFormsUtils::DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
PRBool *aDefaultActionEnabled,
nsIDOMElement *aSrcElement)
{
// it is valid to have aTarget be null if this is an event that must be
// targeted at a model per spec and aSrcElement is non-null. Basically we
// are trying to make sure that we can handle the case where an event needs to
// be sent to the model that doesn't exist, yet (like if the model is
// located at the end of the document and the parser hasn't reached that
// far). In those cases, we'll defer the event dispatch until the model
// exists.
switch (aEvent) {
case eEvent_Previous:
case eEvent_Next:
case eEvent_Focus:
case eEvent_Help:
case eEvent_Hint:
case eEvent_DOMActivate:
case eEvent_ValueChanged:
case eEvent_Valid:
case eEvent_Invalid:
case eEvent_DOMFocusIn:
case eEvent_DOMFocusOut:
case eEvent_Readonly:
case eEvent_Readwrite:
case eEvent_Required:
case eEvent_Optional:
case eEvent_Enabled:
case eEvent_Disabled:
case eEvent_InRange:
case eEvent_OutOfRange:
{
if (!aTarget) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIXFormsControl> control = do_QueryInterface(aTarget);
if (control) {
PRBool acceptableEventTarget = PR_FALSE;
control->IsEventTarget(&acceptableEventTarget);
if (!acceptableEventTarget) {
return NS_OK;
}
}
break;
}
case eEvent_LinkError:
case eEvent_LinkException:
case eEvent_ComputeException:
{
// these events target only models. Verifying that the target
// exists or at least that we have enough information to find the
// model later when the DOM is finished loading
if (!aTarget) {
if (aSrcElement) {
DeferDispatchEvent(aTarget, aEvent, aSrcElement);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIModelElementPrivate> modelPriv(do_QueryInterface(aTarget));
NS_ENSURE_STATE(modelPriv);
PRBool safeToSendEvent = PR_FALSE;
modelPriv->GetHasDOMContentFired(&safeToSendEvent);
if (!safeToSendEvent) {
DeferDispatchEvent(aTarget, aEvent, nsnull);
return NS_OK;
}
break;
}
case eEvent_BindingException:
{
if (!aTarget) {
return NS_ERROR_FAILURE;
}
// we only need special handling for binding exceptions that are
// targeted at bind elements and even then, only if the containing
// model hasn't gotten a DOMContentLoaded, yet. If this is the case,
// we'll defer the notification until XMLEvent handlers are attached.
if (!IsXFormsElement(aTarget, NS_LITERAL_STRING("bind"))) {
break;
}
// look up bind's parent chain looking for the containing model. If
// not found, that is not goodness. Not taking the immediate parent
// in case the form uses nested binds (which is ok on some processors
// for XForms 1.0 and should be ok for all processors in XForms 1.1)
nsCOMPtr<nsIDOMNode> parent, temp = aTarget;
nsCOMPtr<nsIModelElementPrivate> modelPriv;
do {
nsresult rv = temp->GetParentNode(getter_AddRefs(parent));
NS_ENSURE_SUCCESS(rv, rv);
modelPriv = do_QueryInterface(parent);
if (modelPriv) {
break;
}
temp = parent;
} while (temp);
NS_ENSURE_STATE(modelPriv);
PRBool safeToSendEvent = PR_FALSE;
modelPriv->GetHasDOMContentFired(&safeToSendEvent);
if (!safeToSendEvent) {
DeferDispatchEvent(aTarget, aEvent, nsnull);
return NS_OK;
}
break;
}
default:
break;
}
return DispatchXFormsEvent(aTarget, aEvent, aDefaultActionEnabled);
}
/* static */ nsresult
nsXFormsUtils::DispatchDeferredEvents(nsIDOMDocument* aDocument)
{
nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDocument));
NS_ENSURE_STATE(doc);
nsVoidArray *eventList =
NS_STATIC_CAST(nsVoidArray *,
doc->GetProperty(nsXFormsAtoms::deferredEventListProperty));
if (!eventList) {
return NS_OK;
}
PRInt32 count = eventList->Count();
for (PRInt32 i = 0; i < count; i++) {
EventItem *item = (EventItem *)eventList->ElementAt(i);
nsCOMPtr<nsIDOMDocument> objCurrDoc;
if (item->eventTarget) {
item->eventTarget->GetOwnerDocument(getter_AddRefs(objCurrDoc));
} else {
if (item->srcElement) {
item->srcElement->GetOwnerDocument(getter_AddRefs(objCurrDoc));
}
}
if (!objCurrDoc || (objCurrDoc != aDocument))
{
// well the event target or our way to find the event target aren't in
// the document anymore so we probably shouldn't bother to send out the
// event. They are probably on a path to destruction.
delete item;
continue;
}
if (!item->eventTarget) {
// item doesn't have an event target AND it has a srcElement. This
// should only happen in the case where we wanted to dispatch an event to
// a model element that hasn't been parsed, yet. Since
// DispatchDeferredEvents gets called after DOMContentLoaded is
// received by the model, then this should no longer be a problem.
// Go ahead and grab the model from srcElement and make that the
// event target.
if (item->srcElement) {
nsCOMPtr<nsIModelElementPrivate> modelPriv =
nsXFormsUtils::GetModel(item->srcElement);
item->eventTarget = do_QueryInterface(modelPriv);
}
NS_ENSURE_STATE(item->eventTarget);
}
DispatchXFormsEvent(item->eventTarget, item->event, nsnull);
}
doc->DeleteProperty(nsXFormsAtoms::deferredEventListProperty);
return NS_OK;
}
/* static */ nsresult
nsXFormsUtils::SetEventTrusted(nsIDOMEvent* aEvent, nsIDOMNode* aRelatedNode)
{

Просмотреть файл

@ -306,11 +306,19 @@ public:
PRBool *aChanged);
/**
* Dispatch an XForms event. aDefaultActionEnabled is returned indicating
* if the default action of the dispatched event was enabled.
* if the default action of the dispatched event was enabled. aSrcElement
* is passed for events targeted at models. If the model doesn't exist, yet,
* then the event dispatching is deferred. Once DOMContentLoaded is detected
* we'll grab the model from aSrcElement and dispatch the event to that
* model.
*/
static NS_HIDDEN_(nsresult)
DispatchEvent(nsIDOMNode* aTarget, nsXFormsEvent aEvent,
PRBool *aDefaultActionEnabled = nsnull);
PRBool *aDefaultActionEnabled = nsnull,
nsIDOMElement *aSrcElement = nsnull);
static NS_HIDDEN_(nsresult)
DispatchDeferredEvents(nsIDOMDocument* aDocument);
/**
* Sets aEvent trusted if aRelatedNode is in chrome.