Bug 1504911 - part 1: Make all "input" event dispatcher in C++ use new utility method r=smaug

Currently, a lot of code dispatch "input" event and some of them dispatch
"input" event with wrong interface and/or values.  Therefore this patch
creates nsContentUtils::DispatchInputEvent() to make all of them dispatch
correct event.

Unfortunately, due to bug 1506439, we cannot set pointer to refcountable
classes of MOZ_CAN_RUN_SCRIPT method to nullptr.  Therefore, this patch
creates temporary RefPtr<TextEditor> a lot even though it makes damage to
the performance if it's in a hot path.

This patch makes eEditorInput event dispatched with
InternalEditorInputEvent when "input" event should be dispatched with
dom::InputEvent.  However, this patch uses WidgetEvent whose message is
eUnidentifiedEvent and setting WidgetEvent::mSpecifiedEventType to
nsGkAtoms::oninput when "input" event should be dispatched with
dom::Event because we need to keep that eEditorInput and
InternalEditorInputEvent are mapped each other.

Differential Revision: https://phabricator.services.mozilla.com/D12244

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2018-11-21 03:59:02 +00:00
Родитель 388e64d857
Коммит abe138f771
30 изменённых файлов: 304 добавлений и 173 удалений

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

@ -115,16 +115,14 @@ function triggerAutofillAndCheckProfile(profile) {
const checkFieldAutofilled = Promise.all([ const checkFieldAutofilled = Promise.all([
new Promise(resolve => element.addEventListener("input", (event) => { new Promise(resolve => element.addEventListener("input", (event) => {
if (element.tagName == "INPUT" && element.type == "text") { if (element.tagName == "INPUT" && element.type == "text") {
todo(event instanceof InputEvent, ok(event instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface on ${element.tagName}`); `"input" event should be dispatched with InputEvent interface on ${element.tagName}`);
todo_is(event.cancelable, false,
`"input" event should be never cancelable on ${element.tagName}`);
} else { } else {
todo(event instanceof Event && !(event instanceof UIEvent), todo(event instanceof Event && !(event instanceof UIEvent),
`"input" event should be dispatched with Event interface on ${element.tagName}`); `"input" event should be dispatched with Event interface on ${element.tagName}`);
}
is(event.cancelable, false, is(event.cancelable, false,
`"input" event should be never cancelable on ${element.tagName}`); `"input" event should be never cancelable on ${element.tagName}`);
}
is(event.bubbles, true, is(event.bubbles, true,
`"input" event should always bubble on ${element.tagName}`); `"input" event should always bubble on ${element.tagName}`);
resolve(); resolve();

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

@ -68,9 +68,9 @@ async function confirmClear(selector) {
info("Await for clearing input"); info("Await for clearing input");
let promise = new Promise(resolve => let promise = new Promise(resolve =>
document.querySelector(selector).addEventListener("input", (event) => { document.querySelector(selector).addEventListener("input", (event) => {
todo(event instanceof InputEvent, ok(event instanceof InputEvent,
'"input" event should be dispatched with InputEvent interface'); '"input" event should be dispatched with InputEvent interface');
todo_is(event.cancelable, false, is(event.cancelable, false,
'"input" event should be never cancelable'); '"input" event should be never cancelable');
is(event.bubbles, true, is(event.bubbles, true,
'"input" event should always bubble'); '"input" event should always bubble');

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

@ -50,16 +50,14 @@ function checkElementFilled(element, expectedvalue) {
element.addEventListener("input", function onInput(event) { element.addEventListener("input", function onInput(event) {
ok(true, "Checking " + element.name + " field fires input event"); ok(true, "Checking " + element.name + " field fires input event");
if (element.tagName == "INPUT" && element.type == "text") { if (element.tagName == "INPUT" && element.type == "text") {
todo(event instanceof InputEvent, ok(event instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface on ${element.name}`); `"input" event should be dispatched with InputEvent interface on ${element.name}`);
todo_is(event.cancelable, false,
`"input" event should be never cancelable on ${element.name}`);
} else { } else {
todo(event instanceof Event && !(event instanceof UIEvent), todo(event instanceof Event && !(event instanceof UIEvent),
`"input" event should be dispatched with Event interface on ${element.name}`); `"input" event should be dispatched with Event interface on ${element.name}`);
}
is(event.cancelable, false, is(event.cancelable, false,
`"input" event should be never cancelable on ${element.name}`); `"input" event should be never cancelable on ${element.name}`);
}
is(event.bubbles, true, is(event.bubbles, true,
`"input" event should always bubble on ${element.name}`); `"input" event should always bubble on ${element.name}`);
resolve(); resolve();

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

@ -57,6 +57,7 @@
#include "mozilla/dom/HTMLInputElement.h" #include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLSlotElement.h" #include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/dom/HTMLTemplateElement.h" #include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/IDTracker.h" #include "mozilla/dom/IDTracker.h"
#include "mozilla/dom/MouseEventBinding.h" #include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/KeyboardEventBinding.h" #include "mozilla/dom/KeyboardEventBinding.h"
@ -85,6 +86,7 @@
#include "mozilla/dom/Selection.h" #include "mozilla/dom/Selection.h"
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "mozilla/StaticPrefs.h" #include "mozilla/StaticPrefs.h"
#include "mozilla/TextEditor.h"
#include "mozilla/TextEvents.h" #include "mozilla/TextEvents.h"
#include "nsArrayUtils.h" #include "nsArrayUtils.h"
#include "nsAString.h" #include "nsAString.h"
@ -4367,6 +4369,8 @@ nsContentUtils::DispatchTrustedEvent(nsIDocument* aDoc, nsISupports* aTarget,
Composed aComposed, Composed aComposed,
bool* aDefaultAction) bool* aDefaultAction)
{ {
MOZ_ASSERT(!aEventName.EqualsLiteral("input"),
"Use DispatchInputEvent() instead");
return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable, return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
aComposed, Trusted::eYes, aDefaultAction); aComposed, Trusted::eYes, aDefaultAction);
} }
@ -4450,6 +4454,110 @@ nsContentUtils::DispatchEvent(nsIDocument* aDoc, nsISupports* aTarget,
return rv; return rv;
} }
// static
nsresult
nsContentUtils::DispatchInputEvent(Element* aEventTargetElement)
{
RefPtr<TextEditor> textEditor; // See bug 1506439
return DispatchInputEvent(aEventTargetElement, textEditor);
}
// static
nsresult
nsContentUtils::DispatchInputEvent(Element* aEventTargetElement,
TextEditor* aTextEditor)
{
if (NS_WARN_IF(!aEventTargetElement)) {
return NS_ERROR_INVALID_ARG;
}
// If this is called from editor, the instance should be set to aTextEditor.
// Otherwise, we need to look for an editor for aEventTargetElement.
// However, we don't need to do it for HTMLEditor since nobody shouldn't
// dispatch "input" event for HTMLEditor except HTMLEditor itself.
bool useInputEvent = false;
if (aTextEditor) {
useInputEvent = true;
} else if (HTMLTextAreaElement* textAreaElement=
HTMLTextAreaElement::FromNode(aEventTargetElement)) {
aTextEditor = textAreaElement->GetTextEditorWithoutCreation();
useInputEvent = true;
} else if (HTMLInputElement* inputElement =
HTMLInputElement::FromNode(aEventTargetElement)) {
if (inputElement->IsSingleLineTextControl()) {
aTextEditor = inputElement->GetTextEditorWithoutCreation();
useInputEvent = true;
}
}
#ifdef DEBUG
else {
nsCOMPtr<nsITextControlElement> textControlElement =
do_QueryInterface(aEventTargetElement);
MOZ_ASSERT(!textControlElement,
"The event target may have editor, but we've not known it yet.");
}
#endif // #ifdef DEBUG
if (!useInputEvent) {
// Dispatch "input" event with Event instance.
WidgetEvent widgetEvent(true, eUnidentifiedEvent);
widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
widgetEvent.mFlags.mCancelable = false;
// Using same time as nsContentUtils::DispatchEvent() for backward
// compatibility.
widgetEvent.mTime = PR_Now();
(new AsyncEventDispatcher(aEventTargetElement,
widgetEvent))->RunDOMEventWhenSafe();
return NS_OK;
}
nsCOMPtr<nsIWidget> widget;
if (aTextEditor) {
widget = aTextEditor->GetWidget();
if (NS_WARN_IF(!widget)) {
return NS_ERROR_FAILURE;
}
} else {
nsIDocument* document = aEventTargetElement->OwnerDoc();
if (NS_WARN_IF(!document)) {
return NS_ERROR_FAILURE;
}
// If we're running xpcshell tests, we fail to get presShell here.
// Even in such case, we need to dispatch "input" event without widget.
nsIPresShell* presShell = document->GetShell();
if (presShell) {
nsPresContext* presContext = presShell->GetPresContext();
if (NS_WARN_IF(!presContext)) {
return NS_ERROR_FAILURE;
}
widget = presContext->GetRootWidget();
if (NS_WARN_IF(!widget)) {
return NS_ERROR_FAILURE;
}
}
}
// Dispatch "input" event with InputEvent instance.
InternalEditorInputEvent inputEvent(true, eEditorInput, widget);
// Using same time as old event dispatcher in EditorBase for backward
// compatibility.
inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
// If there is an editor, set isComposing to true when it has composition.
// Note that EditorBase::IsIMEComposing() may return false even when we
// need to set it to true.
// Otherwise, i.e., editor hasn't been created for the element yet,
// we should set isComposing to false since the element can never has
// composition without editor.
inputEvent.mIsComposing =
aTextEditor ? !!aTextEditor->GetComposition() : false;
(new AsyncEventDispatcher(aEventTargetElement,
inputEvent))->RunDOMEventWhenSafe();
return NS_OK;
}
nsresult nsresult
nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc, nsContentUtils::DispatchChromeEvent(nsIDocument *aDoc,
nsISupports *aTarget, nsISupports *aTarget,

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

@ -123,6 +123,7 @@ class Dispatcher;
class ErrorResult; class ErrorResult;
class EventListenerManager; class EventListenerManager;
class HTMLEditor; class HTMLEditor;
class TextEditor;
namespace dom { namespace dom {
class ContentFrameMessageManager; class ContentFrameMessageManager;
@ -1377,9 +1378,12 @@ public:
static void MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent); static void MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent);
/** /**
* This method creates and dispatches a trusted event. * These methods create and dispatch a trusted event.
* Works only with events which can be created by calling * Works only with events which can be created by calling
* nsIDocument::CreateEvent() with parameter "Events". * nsIDocument::CreateEvent() with parameter "Events".
* Note that don't use these methods for "input" event. Use
* DispatchInputEvent() instead.
*
* @param aDoc The document which will be used to create the event. * @param aDoc The document which will be used to create the event.
* @param aTarget The target of the event, should be QIable to * @param aTarget The target of the event, should be QIable to
* EventTarget. * EventTarget.
@ -1438,6 +1442,26 @@ public:
aDefaultAction, aOnlyChromeDispatch); aDefaultAction, aOnlyChromeDispatch);
} }
/**
* This method dispatches "input" event with proper event class. If it's
* unsafe to dispatch, this put the event into the script runner queue.
* Input Events spec defines as:
* Input events are dispatched on elements that act as editing hosts,
* including elements with the contenteditable attribute set, textarea
* elements, and input elements that permit text input.
*
* @param aEventTarget The event target element of the "input" event.
* Must not be nullptr.
* @param aTextEditor Optional. If this is called by editor,
* editor should set this. Otherwise, leave
* nullptr.
*/
MOZ_CAN_RUN_SCRIPT
static nsresult DispatchInputEvent(Element* aEventTarget);
MOZ_CAN_RUN_SCRIPT
static nsresult DispatchInputEvent(Element* aEventTarget,
mozilla::TextEditor* aTextEditor);
/** /**
* This method creates and dispatches a untrusted event. * This method creates and dispatches a untrusted event.
* Works only with events which can be created by calling * Works only with events which can be created by calling

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

@ -251,16 +251,12 @@ public:
Unused << NS_WARN_IF(NS_FAILED(DispatchEvents())); Unused << NS_WARN_IF(NS_FAILED(DispatchEvents()));
} }
MOZ_CAN_RUN_SCRIPT_BOUNDARY
nsresult nsresult
DispatchEvents() DispatchEvents()
{ {
nsresult rv = NS_OK; nsresult rv = nsContentUtils::DispatchInputEvent(mInputElement);
rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(), NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch input event");
static_cast<Element*>(mInputElement.get()),
NS_LITERAL_STRING("input"),
CanBubble::eYes,
Cancelable::eNo);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DispatchTrustedEvent failed");
rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(), rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
static_cast<Element*>(mInputElement.get()), static_cast<Element*>(mInputElement.get()),
@ -595,7 +591,9 @@ public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHOD Update(const nsAString& aColor) override; NS_IMETHOD Update(const nsAString& aColor) override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHOD Done(const nsAString& aColor) override; NS_IMETHOD Done(const nsAString& aColor) override;
private: private:
@ -604,6 +602,7 @@ private:
* If aTrustedUpdate is true, it will consider that aColor is a new value. * If aTrustedUpdate is true, it will consider that aColor is a new value.
* Otherwise, it will check that aColor is different from the current value. * Otherwise, it will check that aColor is different from the current value.
*/ */
MOZ_CAN_RUN_SCRIPT
nsresult UpdateInternal(const nsAString& aColor, bool aTrustedUpdate); nsresult UpdateInternal(const nsAString& aColor, bool aTrustedUpdate);
RefPtr<HTMLInputElement> mInput; RefPtr<HTMLInputElement> mInput;
@ -634,15 +633,14 @@ nsColorPickerShownCallback::UpdateInternal(const nsAString& aColor,
} }
} }
if (valueChanged) { if (!valueChanged) {
mValueChanged = true; return NS_OK;
return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
static_cast<Element*>(mInput.get()),
NS_LITERAL_STRING("input"),
CanBubble::eYes,
Cancelable::eNo);
} }
mValueChanged = true;
DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(mInput);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Failed to dispatch input event");
return NS_OK; return NS_OK;
} }
@ -2353,13 +2351,9 @@ HTMLInputElement::SetUserInput(const nsAString& aValue,
nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged); nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
NS_ENSURE_SUCCESS_VOID(rv); NS_ENSURE_SUCCESS_VOID(rv);
// FIXME: We're inconsistent about whether "input" events are cancelable or DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
// not. NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
nsContentUtils::DispatchTrustedEvent(OwnerDoc(), "Failed to dispatch input event");
static_cast<Element*>(this),
NS_LITERAL_STRING("input"),
CanBubble::eYes,
Cancelable::eYes);
// If this element is not currently focused, it won't receive a change event for this // If this element is not currently focused, it won't receive a change event for this
// update through the normal channels. So fire a change event immediately, instead. // update through the normal channels. So fire a change event immediately, instead.
@ -2390,6 +2384,16 @@ HTMLInputElement::GetTextEditor()
return GetTextEditorFromState(); return GetTextEditorFromState();
} }
NS_IMETHODIMP_(TextEditor*)
HTMLInputElement::GetTextEditorWithoutCreation()
{
nsTextEditorState* state = GetEditorState();
if (!state) {
return nullptr;
}
return state->GetTextEditorWithoutCreation();
}
NS_IMETHODIMP_(nsISelectionController*) NS_IMETHODIMP_(nsISelectionController*)
HTMLInputElement::GetSelectionController() HTMLInputElement::GetSelectionController()
{ {
@ -2735,7 +2739,7 @@ HTMLInputElement::SetFiles(FileList* aFiles)
/* static */ void /* static */ void
HTMLInputElement::HandleNumberControlSpin(void* aData) HTMLInputElement::HandleNumberControlSpin(void* aData)
{ {
HTMLInputElement* input = static_cast<HTMLInputElement*>(aData); RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(aData);
NS_ASSERTION(input->mNumberControlSpinnerIsSpinning, NS_ASSERTION(input->mNumberControlSpinnerIsSpinning,
"Should have called nsRepeatService::Stop()"); "Should have called nsRepeatService::Stop()");
@ -3750,12 +3754,9 @@ HTMLInputElement::CancelRangeThumbDrag(bool aIsForUserEvent)
if (frame) { if (frame) {
frame->UpdateForValueChange(); frame->UpdateForValueChange();
} }
RefPtr<AsyncEventDispatcher> asyncDispatcher = DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
new AsyncEventDispatcher(this, NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
NS_LITERAL_STRING("input"), "Failed to dispatch input event");
CanBubble::eYes,
ChromeOnlyDispatch::eNo);
asyncDispatcher->RunDOMEventWhenSafe();
} }
} }
@ -3778,11 +3779,9 @@ HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
} }
if (GetValueAsDecimal() != oldValue) { if (GetValueAsDecimal() != oldValue) {
nsContentUtils::DispatchTrustedEvent(OwnerDoc(), DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
static_cast<Element*>(this), NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
NS_LITERAL_STRING("input"), "Failed to dispatch input event");
CanBubble::eYes,
Cancelable::eNo);
} }
} }
@ -3877,11 +3876,9 @@ HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
SetValueInternal(newVal, nsTextEditorState::eSetValue_BySetUserInput | SetValueInternal(newVal, nsTextEditorState::eSetValue_BySetUserInput |
nsTextEditorState::eSetValue_Notify); nsTextEditorState::eSetValue_Notify);
nsContentUtils::DispatchTrustedEvent(OwnerDoc(), DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
static_cast<Element*>(this), NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
NS_LITERAL_STRING("input"), "Failed to dispatch input event");
CanBubble::eYes,
Cancelable::eNo);
} }
static bool static bool
@ -4091,9 +4088,9 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
} }
} else { } else {
// Fire input event and then change event. // Fire input event and then change event.
nsContentUtils::DispatchTrustedEvent<InternalEditorInputEvent> DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(this);
(OwnerDoc(), static_cast<Element*>(this), NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
eEditorInput, CanBubble::eYes, Cancelable::eNo); "Failed to dispatch input event");
nsContentUtils::DispatchTrustedEvent<WidgetEvent> nsContentUtils::DispatchTrustedEvent<WidgetEvent>
(OwnerDoc(), static_cast<Element*>(this), (OwnerDoc(), static_cast<Element*>(this),

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

@ -187,12 +187,18 @@ public:
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override; void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
virtual nsresult PreHandleEvent(EventChainVisitor& aVisitor) override; virtual nsresult PreHandleEvent(EventChainVisitor& aVisitor) override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual nsresult PostHandleEvent( virtual nsresult PostHandleEvent(
EventChainPostVisitor& aVisitor) override; EventChainPostVisitor& aVisitor) override;
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor); void PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor);
MOZ_CAN_RUN_SCRIPT
void StartRangeThumbDrag(WidgetGUIEvent* aEvent); void StartRangeThumbDrag(WidgetGUIEvent* aEvent);
MOZ_CAN_RUN_SCRIPT
void FinishRangeThumbDrag(WidgetGUIEvent* aEvent = nullptr); void FinishRangeThumbDrag(WidgetGUIEvent* aEvent = nullptr);
MOZ_CAN_RUN_SCRIPT
void CancelRangeThumbDrag(bool aIsForUserEvent = true); void CancelRangeThumbDrag(bool aIsForUserEvent = true);
MOZ_CAN_RUN_SCRIPT
void SetValueOfRangeForUserEvent(Decimal aValue); void SetValueOfRangeForUserEvent(Decimal aValue);
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
@ -223,6 +229,7 @@ public:
NS_IMETHOD_(bool) ValueChanged() const override; NS_IMETHOD_(bool) ValueChanged() const override;
NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override; NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override; NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditorWithoutCreation() override;
NS_IMETHOD_(nsISelectionController*) GetSelectionController() override; NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override; NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override; NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
@ -891,12 +898,14 @@ public:
}; };
void StopNumberControlSpinnerSpin(SpinnerStopState aState = void StopNumberControlSpinnerSpin(SpinnerStopState aState =
eAllowDispatchingEvents); eAllowDispatchingEvents);
MOZ_CAN_RUN_SCRIPT
void StepNumberControlForUserEvent(int32_t aDirection); void StepNumberControlForUserEvent(int32_t aDirection);
/** /**
* The callback function used by the nsRepeatService that we use to spin the * The callback function used by the nsRepeatService that we use to spin the
* spinner for <input type=number>. * spinner for <input type=number>.
*/ */
MOZ_CAN_RUN_SCRIPT_BOUNDARY
static void HandleNumberControlSpin(void* aData); static void HandleNumberControlSpin(void* aData);
bool NumberSpinnerUpButtonIsDepressed() const bool NumberSpinnerUpButtonIsDepressed() const
@ -916,6 +925,7 @@ public:
*/ */
nsIEditor* GetEditor(); nsIEditor* GetEditor();
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void SetUserInput(const nsAString& aInput, void SetUserInput(const nsAString& aInput,
nsIPrincipal& aSubjectPrincipal); nsIPrincipal& aSubjectPrincipal);
@ -1054,6 +1064,7 @@ protected:
/** /**
* Called when an attribute has just been changed * Called when an attribute has just been changed
*/ */
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, const nsAttrValue* aOldValue,
@ -1180,6 +1191,7 @@ protected:
/** /**
* Manages the internal data storage across type changes. * Manages the internal data storage across type changes.
*/ */
MOZ_CAN_RUN_SCRIPT
void HandleTypeChange(uint8_t aNewType, bool aNotify); void HandleTypeChange(uint8_t aNewType, bool aNotify);
/** /**

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

@ -224,6 +224,12 @@ HTMLTextAreaElement::GetTextEditor()
return mState.GetTextEditor(); return mState.GetTextEditor();
} }
NS_IMETHODIMP_(TextEditor*)
HTMLTextAreaElement::GetTextEditorWithoutCreation()
{
return mState.GetTextEditorWithoutCreation();
}
NS_IMETHODIMP_(nsISelectionController*) NS_IMETHODIMP_(nsISelectionController*)
HTMLTextAreaElement::GetSelectionController() HTMLTextAreaElement::GetSelectionController()
{ {

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

@ -83,6 +83,7 @@ public:
NS_IMETHOD_(bool) ValueChanged() const override; NS_IMETHOD_(bool) ValueChanged() const override;
NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override; NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override; NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditorWithoutCreation() override;
NS_IMETHOD_(nsISelectionController*) GetSelectionController() override; NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override; NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override; NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;

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

@ -102,8 +102,12 @@ public:
* Get the editor object associated with the text editor. * Get the editor object associated with the text editor.
* The return value is null if the control does not support an editor * The return value is null if the control does not support an editor
* (for example, if it is a checkbox.) * (for example, if it is a checkbox.)
* Note that GetTextEditor() creates editor if it hasn't been created yet.
* If you need editor only when the editor is there, you should use
* GetTextEditorWithoutCreation().
*/ */
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() = 0; NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() = 0;
NS_IMETHOD_(mozilla::TextEditor*) GetTextEditorWithoutCreation() = 0;
/** /**
* Get the selection controller object associated with the text editor. * Get the selection controller object associated with the text editor.

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

@ -1223,6 +1223,12 @@ nsTextEditorState::GetTextEditor()
return mTextEditor; return mTextEditor;
} }
TextEditor*
nsTextEditorState::GetTextEditorWithoutCreation()
{
return mTextEditor;
}
nsISelectionController* nsISelectionController*
nsTextEditorState::GetSelectionController() const nsTextEditorState::GetSelectionController() const
{ {

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

@ -152,6 +152,7 @@ public:
} }
mozilla::TextEditor* GetTextEditor(); mozilla::TextEditor* GetTextEditor();
mozilla::TextEditor* GetTextEditorWithoutCreation();
nsISelectionController* GetSelectionController() const; nsISelectionController* GetSelectionController() const;
nsFrameSelection* GetConstFrameSelection(); nsFrameSelection* GetConstFrameSelection();
nsresult BindToFrame(nsTextControlFrame* aFrame); nsresult BindToFrame(nsTextControlFrame* aFrame);

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

@ -164,13 +164,18 @@ SimpleTest.waitForFocus(() => {
} }
if (inputEvents.length > 0) { if (inputEvents.length > 0) {
if (test.result.useInputEvent) { if (test.result.useInputEvent) {
if (test.type === "number" || test.type === "time") {
todo(inputEvents[0] instanceof InputEvent, todo(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`); `"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
} else {
ok(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
}
} else { } else {
ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent), ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent),
`"input" event should be dispatched with Event interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`); `"input" event should be dispatched with Event interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
} }
todo_is(inputEvents[0].cancelable, false, is(inputEvents[0].cancelable, false,
`"input" event should be never cancelable (${tag}, before getting focus)`); `"input" event should be never cancelable (${tag}, before getting focus)`);
is(inputEvents[0].bubbles, true, is(inputEvents[0].bubbles, true,
`"input" event should always bubble (${tag}, before getting focus)`); `"input" event should always bubble (${tag}, before getting focus)`);
@ -222,13 +227,18 @@ SimpleTest.waitForFocus(() => {
} }
if (inputEvents.length > 0) { if (inputEvents.length > 0) {
if (test.result.useInputEvent) { if (test.result.useInputEvent) {
if (test.type === "number" || test.type === "time") {
todo(inputEvents[0] instanceof InputEvent, todo(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`); `"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
} else {
ok(inputEvents[0] instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
}
} else { } else {
ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent), ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent),
`"input" event should be dispatched with Event interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`); `"input" event should be dispatched with Event interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
} }
todo_is(inputEvents[0].cancelable, false, is(inputEvents[0].cancelable, false,
`"input" event should be never cancelable (${tag}, after getting focus)`); `"input" event should be never cancelable (${tag}, after getting focus)`);
is(inputEvents[0].bubbles, true, is(inputEvents[0].bubbles, true,
`"input" event should always bubble (${tag}, after getting focus)`); `"input" event should always bubble (${tag}, after getting focus)`);

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

@ -57,13 +57,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
} }
function checkIfInputIsEvent(aEvent, aDescription) { function checkIfInputIsEvent(aEvent, aDescription) {
if (event.target.type === "checkbox" || event.target.type === "radio") {
todo(event instanceof Event && !(event instanceof UIEvent),
`"input" event should be dispatched with InputEvent interface ${aDescription}`);
} else {
ok(event instanceof Event && !(event instanceof UIEvent), ok(event instanceof Event && !(event instanceof UIEvent),
`"input" event should be dispatched with InputEvent interface ${aDescription}`); `"input" event should be dispatched with InputEvent interface ${aDescription}`);
}
is(aEvent.cancelable, false, is(aEvent.cancelable, false,
`"input" event should be never cancelable ${aDescription}`); `"input" event should be never cancelable ${aDescription}`);
is(aEvent.bubbles, true, is(aEvent.bubbles, true,

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

@ -2142,56 +2142,6 @@ EditorBase::NotifySelectionChanged(nsIDocument* aDocument,
return NS_OK; return NS_OK;
} }
class EditorInputEventDispatcher final : public Runnable
{
public:
EditorInputEventDispatcher(EditorBase* aEditorBase,
nsIContent* aTarget,
bool aIsComposing)
: Runnable("EditorInputEventDispatcher")
, mEditorBase(aEditorBase)
, mTarget(aTarget)
, mIsComposing(aIsComposing)
{
}
NS_IMETHOD Run() override
{
// Note that we don't need to check mDispatchInputEvent here. We need
// to check it only when the editor requests to dispatch the input event.
if (!mTarget->IsInComposedDoc()) {
return NS_OK;
}
nsCOMPtr<nsIPresShell> ps = mEditorBase->GetPresShell();
if (!ps) {
return NS_OK;
}
nsCOMPtr<nsIWidget> widget = mEditorBase->GetWidget();
if (!widget) {
return NS_OK;
}
// Even if the change is caused by untrusted event, we need to dispatch
// trusted input event since it's a fact.
InternalEditorInputEvent inputEvent(true, eEditorInput, widget);
inputEvent.mTime = static_cast<uint64_t>(PR_Now() / 1000);
inputEvent.mIsComposing = mIsComposing;
nsEventStatus status = nsEventStatus_eIgnore;
nsresult rv =
ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status);
NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error
return NS_OK;
}
private:
RefPtr<EditorBase> mEditorBase;
nsCOMPtr<nsIContent> mTarget;
bool mIsComposing;
};
void void
EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification) EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification)
{ {
@ -2252,19 +2202,15 @@ EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification)
void void
EditorBase::FireInputEvent() EditorBase::FireInputEvent()
{ {
// We don't need to dispatch multiple input events if there is a pending RefPtr<Element> targetElement = GetInputEventTargetElement();
// input event. However, it may have different event target. If we resolved if (NS_WARN_IF(!targetElement)) {
// this issue, we need to manage the pending events in an array. But it's return;
// overwork. We don't need to do it for the very rare case. }
RefPtr<TextEditor> textEditor = AsTextEditor();
nsCOMPtr<nsIContent> target = GetInputEventTargetContent(); DebugOnly<nsresult> rvIgnored =
NS_ENSURE_TRUE_VOID(target); nsContentUtils::DispatchInputEvent(targetElement, textEditor);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
// NOTE: Don't refer IsIMEComposing() because it returns false even before "Failed to dispatch input event");
// compositionend. However, DOM Level 3 Events defines it should be
// true after compositionstart and before compositionend.
nsContentUtils::AddScriptRunner(
new EditorInputEventDispatcher(this, target, !!GetComposition()));
} }
NS_IMETHODIMP NS_IMETHODIMP

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

@ -356,6 +356,7 @@ public:
/** /**
* ToggleTextDirection() toggles text-direction of the root element. * ToggleTextDirection() toggles text-direction of the root element.
*/ */
MOZ_CAN_RUN_SCRIPT_BOUNDARY
nsresult ToggleTextDirection(); nsresult ToggleTextDirection();
/** /**
@ -367,6 +368,7 @@ public:
eLTR, eLTR,
eRTL, eRTL,
}; };
MOZ_CAN_RUN_SCRIPT
void SwitchTextDirectionTo(TextDirection aTextDirection); void SwitchTextDirectionTo(TextDirection aTextDirection);
/** /**
@ -1732,7 +1734,9 @@ protected: // Called by helper classes.
* can later merge, if needed. Merging is unavailable between transaction * can later merge, if needed. Merging is unavailable between transaction
* manager batches. * manager batches.
*/ */
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void BeginPlaceholderTransaction(nsAtom* aTransactionName); void BeginPlaceholderTransaction(nsAtom* aTransactionName);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
void EndPlaceholderTransaction(); void EndPlaceholderTransaction();
void BeginUpdateViewBatch(); void BeginUpdateViewBatch();
@ -1768,6 +1772,8 @@ protected: // Shouldn't be used by friend classes
virtual nsresult SelectAllInternal(); virtual nsresult SelectAllInternal();
nsresult DetermineCurrentDirection(); nsresult DetermineCurrentDirection();
MOZ_CAN_RUN_SCRIPT
void FireInputEvent(); void FireInputEvent();
/** /**
@ -1888,7 +1894,7 @@ protected: // Shouldn't be used by friend classes
/** /**
* Get the input event target. This might return null. * Get the input event target. This might return null.
*/ */
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0; virtual already_AddRefed<Element> GetInputEventTargetElement() = 0;
/** /**
* Return true if spellchecking should be enabled for this editor. * Return true if spellchecking should be enabled for this editor.
@ -1950,6 +1956,7 @@ protected: // Shouldn't be used by friend classes
eNotifyEditorObserversOfBefore, eNotifyEditorObserversOfBefore,
eNotifyEditorObserversOfCancel eNotifyEditorObserversOfCancel
}; };
MOZ_CAN_RUN_SCRIPT
void NotifyEditorObservers(NotificationForEditorObservers aNotification); void NotifyEditorObservers(NotificationForEditorObservers aNotification);
private: private:

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

@ -1031,7 +1031,7 @@ EditorEventListener::HandleChangeComposition(
return NS_OK; return NS_OK;
} }
TextEditor* textEditor = editorBase->AsTextEditor(); RefPtr<TextEditor> textEditor = editorBase->AsTextEditor();
return textEditor->OnCompositionChange(*aCompositionChangeEvent); return textEditor->OnCompositionChange(*aCompositionChangeEvent);
} }
@ -1050,7 +1050,7 @@ EditorEventListener::HandleEndComposition(
MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(), MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
"eCompositionEnd shouldn't be cancelable"); "eCompositionEnd shouldn't be cancelable");
TextEditor* textEditor = editorBase->AsTextEditor(); RefPtr<TextEditor> textEditor = editorBase->AsTextEditor();
textEditor->OnCompositionEnd(*aCompositionEndEvent); textEditor->OnCompositionEnd(*aCompositionEndEvent);
} }

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

@ -63,11 +63,14 @@ protected:
#ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
nsresult KeyDown(const WidgetKeyboardEvent* aKeyboardEvent); nsresult KeyDown(const WidgetKeyboardEvent* aKeyboardEvent);
MOZ_CAN_RUN_SCRIPT
nsresult KeyUp(const WidgetKeyboardEvent* aKeyboardEvent); nsresult KeyUp(const WidgetKeyboardEvent* aKeyboardEvent);
#endif #endif
nsresult KeyPress(WidgetKeyboardEvent* aKeyboardEvent); nsresult KeyPress(WidgetKeyboardEvent* aKeyboardEvent);
MOZ_CAN_RUN_SCRIPT
nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent); nsresult HandleChangeComposition(WidgetCompositionEvent* aCompositionEvent);
nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent); nsresult HandleStartComposition(WidgetCompositionEvent* aCompositionEvent);
MOZ_CAN_RUN_SCRIPT
void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent); void HandleEndComposition(WidgetCompositionEvent* aCompositionEvent);
MOZ_CAN_RUN_SCRIPT MOZ_CAN_RUN_SCRIPT
virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent); virtual nsresult MouseDown(dom::MouseEvent* aMouseEvent);

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

@ -5454,10 +5454,10 @@ HTMLEditor::GetPreferredIMEState(IMEState* aState)
return NS_OK; return NS_OK;
} }
already_AddRefed<nsIContent> already_AddRefed<Element>
HTMLEditor::GetInputEventTargetContent() HTMLEditor::GetInputEventTargetElement()
{ {
nsCOMPtr<nsIContent> target = GetActiveEditingHost(); RefPtr<Element> target = GetActiveEditingHost();
return target.forget(); return target.forget();
} }

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

@ -1778,7 +1778,7 @@ protected: // Shouldn't be used by friend classes
*/ */
already_AddRefed<nsINode> GetFocusedNode(); already_AddRefed<nsINode> GetFocusedNode();
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override; virtual already_AddRefed<Element> GetInputEventTargetElement() override;
/** /**
* Return TRUE if aElement is a table-related elemet and caret was set. * Return TRUE if aElement is a table-related elemet and caret was set.

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

@ -1460,10 +1460,10 @@ TextEditor::OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent)
NotifyEditorObservers(eNotifyEditorObserversOfEnd); NotifyEditorObservers(eNotifyEditorObserversOfEnd);
} }
already_AddRefed<nsIContent> already_AddRefed<Element>
TextEditor::GetInputEventTargetContent() TextEditor::GetInputEventTargetElement()
{ {
nsCOMPtr<nsIContent> target = do_QueryInterface(mEventTarget); nsCOMPtr<Element> target = do_QueryInterface(mEventTarget);
return target.forget(); return target.forget();
} }

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

@ -64,7 +64,9 @@ public:
// If there are some good name to create non-virtual Undo()/Redo() methods, // If there are some good name to create non-virtual Undo()/Redo() methods,
// we should create them and those methods should just run them. // we should create them and those methods should just run them.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHOD Undo(uint32_t aCount) final; NS_IMETHOD Undo(uint32_t aCount) final;
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHOD Redo(uint32_t aCount) final; NS_IMETHOD Redo(uint32_t aCount) final;
NS_IMETHOD Cut() override; NS_IMETHOD Cut() override;
@ -205,6 +207,7 @@ public:
* @param aCompositionChangeEvent eCompositionChange event which should * @param aCompositionChangeEvent eCompositionChange event which should
* be handled in this editor. * be handled in this editor.
*/ */
MOZ_CAN_RUN_SCRIPT
nsresult nsresult
OnCompositionChange(WidgetCompositionEvent& aCompositionChangeEvent); OnCompositionChange(WidgetCompositionEvent& aCompositionChangeEvent);
@ -213,6 +216,7 @@ public:
* event and it's followed by eCompositionEnd event and after * event and it's followed by eCompositionEnd event and after
* OnCompositionChange() is called. * OnCompositionChange() is called.
*/ */
MOZ_CAN_RUN_SCRIPT
void OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent); void OnCompositionEnd(WidgetCompositionEvent& aCompositionEndEvent);
/** /**
@ -530,7 +534,7 @@ protected: // Shouldn't be used by friend classes
*/ */
bool EnsureComposition(WidgetCompositionEvent& aCompositionEvent); bool EnsureComposition(WidgetCompositionEvent& aCompositionEvent);
virtual already_AddRefed<nsIContent> GetInputEventTargetContent() override; virtual already_AddRefed<Element> GetInputEventTargetElement() override;
protected: protected:
mutable nsCOMPtr<nsIDocumentEncoder> mCachedDocumentEncoder; mutable nsCOMPtr<nsIDocumentEncoder> mCachedDocumentEncoder;

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

@ -128,6 +128,7 @@ public:
* @param aRepaint if true then force repaint (NOTE: we always force repaint currently) * @param aRepaint if true then force repaint (NOTE: we always force repaint currently)
* @note This method might destroy |this|. * @note This method might destroy |this|.
*/ */
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual void SetFocus(bool aOn, bool aRepaint) override; virtual void SetFocus(bool aOn, bool aRepaint) override;
bool IsDroppedDown() { return mDroppedDown; } bool IsDroppedDown() { return mDroppedDown; }

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

@ -459,11 +459,11 @@ nsFileControlFrame::DnDListener::HandleEvent(Event* aEvent)
inputElement->SetFiles(fileList, true); inputElement->SetFiles(fileList, true);
} }
nsContentUtils::DispatchTrustedEvent(inputElement->OwnerDoc(), RefPtr<TextEditor> textEditor;
static_cast<nsINode*>(inputElement), DebugOnly<nsresult> rvIgnored =
NS_LITERAL_STRING("input"), nsContentUtils::DispatchInputEvent(inputElement);
CanBubble::eYes, NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
Cancelable::eNo); "Failed to dispatch input event");
nsContentUtils::DispatchTrustedEvent(inputElement->OwnerDoc(), nsContentUtils::DispatchTrustedEvent(inputElement->OwnerDoc(),
static_cast<nsINode*>(inputElement), static_cast<nsINode*>(inputElement),
NS_LITERAL_STRING("change"), NS_LITERAL_STRING("change"),

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

@ -123,7 +123,9 @@ protected:
: MouseListener(aFrame) : MouseListener(aFrame)
{} {}
NS_DECL_NSIDOMEVENTLISTENER // nsIDOMEventListener
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHOD HandleEvent(mozilla::dom::Event* aEvent) override;
nsresult GetBlobImplForWebkitDirectory(mozilla::dom::FileList* aFileList, nsresult GetBlobImplForWebkitDirectory(mozilla::dom::FileList* aFileList,
mozilla::dom::BlobImpl** aBlobImpl); mozilla::dom::BlobImpl** aBlobImpl);

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

@ -70,7 +70,10 @@ public:
void SetFrame(nsListControlFrame *aFrame) { mFrame = aFrame; } void SetFrame(nsListControlFrame *aFrame) { mFrame = aFrame; }
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
// nsIDOMEventListener
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_IMETHOD HandleEvent(Event* aEvent) override;
private: private:
~nsListEventListener() {} ~nsListEventListener() {}
@ -1390,14 +1393,17 @@ nsListControlFrame::FireOnInputAndOnChange()
} }
} }
nsCOMPtr<nsIContent> content = mContent; RefPtr<Element> element = Element::FromNodeOrNull(mContent);
if (NS_WARN_IF(!element)) {
return;
}
// Dispatch the input event. // Dispatch the input event.
nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content, DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(element);
NS_LITERAL_STRING("input"), NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
CanBubble::eYes, "Failed to dispatch input event");
Cancelable::eNo);
// Dispatch the change event. // Dispatch the change event.
nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content, nsContentUtils::DispatchTrustedEvent(element->OwnerDoc(), element,
NS_LITERAL_STRING("change"), NS_LITERAL_STRING("change"),
CanBubble::eYes, CanBubble::eYes,
Cancelable::eNo); Cancelable::eNo);

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

@ -134,6 +134,7 @@ public:
* Dispatch a DOM oninput and onchange event synchroniously. * Dispatch a DOM oninput and onchange event synchroniously.
* @note This method might destroy the frame, pres shell and other objects. * @note This method might destroy the frame, pres shell and other objects.
*/ */
MOZ_CAN_RUN_SCRIPT
void FireOnInputAndOnChange(); void FireOnInputAndOnChange();
/** /**
@ -160,10 +161,13 @@ public:
* @note These methods might destroy the frame, pres shell and other objects. * @note These methods might destroy the frame, pres shell and other objects.
*/ */
nsresult MouseDown(mozilla::dom::Event* aMouseEvent); nsresult MouseDown(mozilla::dom::Event* aMouseEvent);
MOZ_CAN_RUN_SCRIPT
nsresult MouseUp(mozilla::dom::Event* aMouseEvent); nsresult MouseUp(mozilla::dom::Event* aMouseEvent);
nsresult MouseMove(mozilla::dom::Event* aMouseEvent); nsresult MouseMove(mozilla::dom::Event* aMouseEvent);
nsresult DragMove(mozilla::dom::Event* aMouseEvent); nsresult DragMove(mozilla::dom::Event* aMouseEvent);
MOZ_CAN_RUN_SCRIPT
nsresult KeyDown(mozilla::dom::Event* aKeyEvent); nsresult KeyDown(mozilla::dom::Event* aKeyEvent);
MOZ_CAN_RUN_SCRIPT
nsresult KeyPress(mozilla::dom::Event* aKeyEvent); nsresult KeyPress(mozilla::dom::Event* aKeyEvent);
/** /**
@ -257,6 +261,7 @@ protected:
* @note This method might destroy the frame, pres shell and other objects. * @note This method might destroy the frame, pres shell and other objects.
* Returns false if calling it destroyed |this|. * Returns false if calling it destroyed |this|.
*/ */
MOZ_CAN_RUN_SCRIPT
bool UpdateSelection(); bool UpdateSelection();
/** /**
@ -271,6 +276,7 @@ protected:
* Toggles (show/hide) the combobox dropdown menu. * Toggles (show/hide) the combobox dropdown menu.
* @note This method might destroy the frame, pres shell and other objects. * @note This method might destroy the frame, pres shell and other objects.
*/ */
MOZ_CAN_RUN_SCRIPT
void DropDownToggleKey(mozilla::dom::Event* aKeyEvent); void DropDownToggleKey(mozilla::dom::Event* aKeyEvent);
/** /**
@ -378,6 +384,7 @@ protected:
bool HandleListSelection(mozilla::dom::Event * aDOMEvent, bool HandleListSelection(mozilla::dom::Event * aDOMEvent,
int32_t selectedIndex); int32_t selectedIndex);
void InitSelectionRange(int32_t aClickedIndex); void InitSelectionRange(int32_t aClickedIndex);
MOZ_CAN_RUN_SCRIPT
void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode, void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode,
bool aIsShift, bool aIsControlOrMeta); bool aIsShift, bool aIsControlOrMeta);

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

@ -35,9 +35,9 @@ function handleSubmit() { // eslint-disable-line no-unused-vars
function handleInput(aEvent) { function handleInput(aEvent) {
info("Input"); info("Input");
is(input.value, expectedValue, "Check input value"); is(input.value, expectedValue, "Check input value");
todo(aEvent instanceof InputEvent, ok(aEvent instanceof InputEvent,
'"input" event should be dispatched with InputEvent interface'); '"input" event should be dispatched with InputEvent interface');
todo_is(aEvent.cancelable, false, is(aEvent.cancelable, false,
'"input" event should be never cancelable'); '"input" event should be never cancelable');
is(aEvent.bubbles, true, is(aEvent.bubbles, true,
'"input" event should always bubble'); '"input" event should always bubble');

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

@ -95,19 +95,12 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
if (aTest.inputEvents[i].todoInterfaceOnXUL && aInputEvents[i].target.tagName === "textbox") { if (aTest.inputEvents[i].todoInterfaceOnXUL && aInputEvents[i].target.tagName === "textbox") {
this._todo_is(aInputEvents[i] instanceof this._window.InputEvent, true, this._todo_is(aInputEvents[i] instanceof this._window.InputEvent, true,
this._description + ", " + aTest.description + ': "input" event should be dispatched with InputEvent interface'); this._description + ", " + aTest.description + ': "input" event should be dispatched with InputEvent interface');
this._is(aInputEvents[i].cancelable, false,
this._description + ", " + aTest.description + ': "input" event should be never cancelable');
} else if (aTest.inputEvents[i].inputType === "insertReplacementText") {
this._todo_is(aInputEvents[i] instanceof this._window.InputEvent, true,
this._description + ", " + aTest.description + ': "input" event should be dispatched with InputEvent interface');
this._todo_is(aInputEvents[i].cancelable, false,
this._description + ", " + aTest.description + ': "input" event should be never cancelable');
} else { } else {
this._is(aInputEvents[i] instanceof this._window.InputEvent, true, this._is(aInputEvents[i] instanceof this._window.InputEvent, true,
this._description + ", " + aTest.description + ': "input" event should be dispatched with InputEvent interface'); this._description + ", " + aTest.description + ': "input" event should be dispatched with InputEvent interface');
}
this._is(aInputEvents[i].cancelable, false, this._is(aInputEvents[i].cancelable, false,
this._description + ", " + aTest.description + ': "input" event should be never cancelable'); this._description + ", " + aTest.description + ': "input" event should be never cancelable');
}
this._is(aInputEvents[i].bubbles, true, this._is(aInputEvents[i].bubbles, true,
this._description + ", " + aTest.description + ': "input" event should always bubble'); this._description + ", " + aTest.description + ': "input" event should always bubble');
} }

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

@ -510,7 +510,8 @@ private:
mFlags.mBubbles = true; mFlags.mBubbles = true;
break; break;
default: default:
if (mMessage == eResize) { if (mMessage == eResize ||
mMessage == eEditorInput) {
mFlags.mCancelable = false; mFlags.mCancelable = false;
} else { } else {
mFlags.mCancelable = true; mFlags.mCancelable = true;
@ -614,7 +615,8 @@ public:
// If JS creates an event with unknown event type or known event type but // If JS creates an event with unknown event type or known event type but
// for different event interface, the event type is stored to this. // for different event interface, the event type is stored to this.
// NOTE: This is always used if the instance is a WidgetCommandEvent instance. // NOTE: This is always used if the instance is a WidgetCommandEvent instance
// or "input" event is dispatched with dom::Event class.
RefPtr<nsAtom> mSpecifiedEventType; RefPtr<nsAtom> mSpecifiedEventType;
// nsAtom isn't available on non-main thread due to unsafe. Therefore, // nsAtom isn't available on non-main thread due to unsafe. Therefore,