diff --git a/docshell/base/nsDocShellTreeOwner.cpp b/docshell/base/nsDocShellTreeOwner.cpp index 42c8a073005a..c9109f5f3704 100644 --- a/docshell/base/nsDocShellTreeOwner.cpp +++ b/docshell/base/nsDocShellTreeOwner.cpp @@ -924,6 +924,10 @@ nsDocShellTreeOwner::HandleEvent(Event* aEvent) { handler->CanDropLink(dragEvent, false, &canDropLink); if (canDropLink) { aEvent->PreventDefault(); + WidgetDragEvent* asWidgetDropEvent = + dragEvent->WidgetEventPtr()->AsDragEvent(); + asWidgetDropEvent->UpdateDefaultPreventedOnContent( + asWidgetDropEvent->mCurrentTarget); } } else if (eventType.EqualsLiteral("drop")) { nsIWebNavigation* webnav = static_cast(mWebBrowser); @@ -974,6 +978,10 @@ nsDocShellTreeOwner::HandleEvent(Event* aEvent) { } else { aEvent->StopPropagation(); aEvent->PreventDefault(); + WidgetDragEvent* asWidgetDropEvent = + dragEvent->WidgetEventPtr()->AsDragEvent(); + asWidgetDropEvent->UpdateDefaultPreventedOnContent( + asWidgetDropEvent->mCurrentTarget); } } diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index fc507de698d4..dd272f133703 100644 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -436,26 +436,13 @@ void Event::PreventDefaultInternal(bool aCalledByDefaultHandler, return; } - WidgetDragEvent* dragEvent = mEvent->AsDragEvent(); - if (!dragEvent) { - return; - } - - nsIPrincipal* principal = nullptr; - nsCOMPtr node = - nsINode::FromEventTargetOrNull(mEvent->mCurrentTarget); - if (node) { - principal = node->NodePrincipal(); - } else { - nsCOMPtr sop = - do_QueryInterface(mEvent->mCurrentTarget); - if (sop) { - principal = sop->GetPrincipal(); + // If this is called by default handlers, the caller will call + // UpdateDefaultPreventedOnContentFor when necessary. + if (!aCalledByDefaultHandler) { + if (WidgetDragEvent* dragEvent = mEvent->AsDragEvent()) { + dragEvent->UpdateDefaultPreventedOnContent(dragEvent->mCurrentTarget); } } - if (principal && !principal->IsSystemPrincipal()) { - dragEvent->mDefaultPreventedOnContent = true; - } } void Event::SetEventType(const nsAString& aEventTypeArg) { diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index fe9e0ba35b41..aceee7f08278 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -1555,4 +1555,33 @@ void EventDispatcher::GetComposedPathFor(WidgetEvent* aEvent, } } +void EventChainPreVisitor::IgnoreCurrentTargetBecauseOfShadowDOMRetargeting() { + mCanHandle = false; + mIgnoreBecauseOfShadowDOM = true; + + EventTarget* target = nullptr; + + auto getWindow = [this]() -> nsPIDOMWindowOuter* { + nsINode* node = nsINode::FromEventTargetOrNull(this->mParentTarget); + if (!node) { + return nullptr; + } + Document* doc = node->GetComposedDoc(); + if (!doc) { + return nullptr; + } + + return doc->GetWindow(); + }; + + // The HTMLEditor is registered to nsWindowRoot, so we + // want to dispatch events to it. + if (nsCOMPtr win = getWindow()) { + target = win->GetParentTarget(); + } + SetParentTarget(target, false); + + mEventTargetAtParent = nullptr; +} + } // namespace mozilla diff --git a/dom/events/EventDispatcher.h b/dom/events/EventDispatcher.h index 9a9a0f2361f8..85fe45b90018 100644 --- a/dom/events/EventDispatcher.h +++ b/dom/events/EventDispatcher.h @@ -169,12 +169,7 @@ class MOZ_STACK_CLASS EventChainPreVisitor final : public EventChainVisitor { } } - void IgnoreCurrentTargetBecauseOfShadowDOMRetargeting() { - mCanHandle = false; - mIgnoreBecauseOfShadowDOM = true; - SetParentTarget(nullptr, false); - mEventTargetAtParent = nullptr; - } + void IgnoreCurrentTargetBecauseOfShadowDOMRetargeting(); /** * Member that must be set in GetEventTargetParent by event targets. If set to diff --git a/dom/events/MouseEvent.cpp b/dom/events/MouseEvent.cpp index 069e5531b79a..49b1d493aad4 100644 --- a/dom/events/MouseEvent.cpp +++ b/dom/events/MouseEvent.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/MouseEvent.h" #include "mozilla/MouseEvents.h" +#include "mozilla/BasePrincipal.h" #include "nsContentUtils.h" #include "nsIContent.h" #include "nsIScreenManager.h" @@ -310,6 +311,25 @@ uint16_t MouseEvent::MozInputSource() const { } // namespace mozilla::dom using namespace mozilla; + +void WidgetDragEvent::UpdateDefaultPreventedOnContent( + dom::EventTarget* aTarget) { + MOZ_ASSERT(DefaultPrevented()); + nsIPrincipal* principal = nullptr; + nsINode* node = nsINode::FromEventTargetOrNull(aTarget); + if (node) { + principal = node->NodePrincipal(); + } else { + nsCOMPtr sop = do_QueryInterface(aTarget); + if (sop) { + principal = sop->GetPrincipal(); + } + } + if (principal && !principal->IsSystemPrincipal()) { + mDefaultPreventedOnContent = true; + } +} + using namespace mozilla::dom; already_AddRefed NS_NewDOMMouseEvent(EventTarget* aOwner, diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 0f864a4bb29a..73e1d65ac72d 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -5609,8 +5609,9 @@ nsresult EditorBase::FinalizeSelection() { // TODO: Running script from here makes harder to handle blur events. We // should do this asynchronously. focusManager->UpdateCaretForCaretBrowsingMode(); - if (nsCOMPtr node = do_QueryInterface(GetDOMEventTarget())) { - if (node->OwnerDoc()->GetUnretargetedFocusedContent() != node) { + if (Element* rootElement = GetExposedRoot()) { + if (rootElement->OwnerDoc()->GetUnretargetedFocusedContent() != + rootElement) { selectionController->SelectionWillLoseFocus(); } else { // We leave this selection as the focused one. When the focus returns, it @@ -5916,6 +5917,19 @@ bool EditorBase::CanKeepHandlingFocusEvent( if (!focusManager->GetFocusedElement()) { return false; } + + // If there's an HTMLEditor registered in the target document and we + // are not that HTMLEditor (for cases like nested documents), let + // that HTMLEditor to handle the focus event. + if (IsHTMLEditor()) { + const HTMLEditor* precedentHTMLEditor = + aOriginalEventTargetNode.OwnerDoc()->GetHTMLEditor(); + + if (precedentHTMLEditor && precedentHTMLEditor != this) { + return false; + } + } + const nsIContent* exposedTargetContent = aOriginalEventTargetNode.AsContent() ->FindFirstNonChromeOnlyAccessContent(); diff --git a/editor/libeditor/EditorEventListener.cpp b/editor/libeditor/EditorEventListener.cpp index ad4d15847bbb..3799524023f1 100644 --- a/editor/libeditor/EditorEventListener.cpp +++ b/editor/libeditor/EditorEventListener.cpp @@ -28,7 +28,8 @@ #include "mozilla/dom/Element.h" // for Element #include "mozilla/dom/Event.h" // for Event #include "mozilla/dom/EventTarget.h" // for EventTarget -#include "mozilla/dom/MouseEvent.h" // for MouseEvent +#include "mozilla/dom/HTMLTextAreaElement.h" +#include "mozilla/dom/MouseEvent.h" // for MouseEvent #include "mozilla/dom/Selection.h" #include "nsAString.h" @@ -154,20 +155,21 @@ nsresult EditorEventListener::InstallToEditor() { return NS_ERROR_FAILURE; } + // For non-html editor, ie.TextEditor, we want to preserve + // the event handling order to ensure listeners that are + // added to and still working as expected. + EventListenerFlags flags = mEditorBase->IsHTMLEditor() + ? TrustedEventsAtSystemGroupCapture() + : TrustedEventsAtSystemGroupBubble(); #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH - eventListenerManager->AddEventListenerByType( - this, u"keydown"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->AddEventListenerByType( - this, u"keyup"_ns, TrustedEventsAtSystemGroupBubble()); + eventListenerManager->AddEventListenerByType(this, u"keydown"_ns, flags); + eventListenerManager->AddEventListenerByType(this, u"keyup"_ns, flags); #endif - eventListenerManager->AddEventListenerByType( - this, u"keypress"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->AddEventListenerByType( - this, u"dragover"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->AddEventListenerByType( - this, u"dragleave"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->AddEventListenerByType( - this, u"drop"_ns, TrustedEventsAtSystemGroupBubble()); + + eventListenerManager->AddEventListenerByType(this, u"keypress"_ns, flags); + eventListenerManager->AddEventListenerByType(this, u"dragover"_ns, flags); + eventListenerManager->AddEventListenerByType(this, u"dragleave"_ns, flags); + eventListenerManager->AddEventListenerByType(this, u"drop"_ns, flags); // XXX We should add the mouse event listeners as system event group. // E.g., web applications cannot prevent middle mouse paste by // preventDefault() of click event at bubble phase. @@ -236,20 +238,17 @@ void EditorEventListener::UninstallFromEditor() { return; } + EventListenerFlags flags = mEditorBase->IsHTMLEditor() + ? TrustedEventsAtSystemGroupCapture() + : TrustedEventsAtSystemGroupBubble(); #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH - eventListenerManager->RemoveEventListenerByType( - this, u"keydown"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->RemoveEventListenerByType( - this, u"keyup"_ns, TrustedEventsAtSystemGroupBubble()); + eventListenerManager->RemoveEventListenerByType(this, u"keydown"_ns, flags); + eventListenerManager->RemoveEventListenerByType(this, u"keyup"_ns, flags); #endif - eventListenerManager->RemoveEventListenerByType( - this, u"keypress"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->RemoveEventListenerByType( - this, u"dragover"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->RemoveEventListenerByType( - this, u"dragleave"_ns, TrustedEventsAtSystemGroupBubble()); - eventListenerManager->RemoveEventListenerByType( - this, u"drop"_ns, TrustedEventsAtSystemGroupBubble()); + eventListenerManager->RemoveEventListenerByType(this, u"keypress"_ns, flags); + eventListenerManager->RemoveEventListenerByType(this, u"dragover"_ns, flags); + eventListenerManager->RemoveEventListenerByType(this, u"dragleave"_ns, flags); + eventListenerManager->RemoveEventListenerByType(this, u"drop"_ns, flags); eventListenerManager->RemoveEventListenerByType(this, u"mousedown"_ns, TrustedEventsAtCapture()); eventListenerManager->RemoveEventListenerByType(this, u"mouseup"_ns, @@ -315,10 +314,36 @@ NS_IMETHODIMP EditorEventListener::HandleEvent(Event* aEvent) { // each event handler would just ignore the event. So, in this method, // you don't need to check if the QI succeeded before each call. WidgetEvent* internalEvent = aEvent->WidgetEventPtr(); + + if (DetachedFromEditor()) { + return NS_OK; + } + + // For nested documents with multiple HTMLEditor registered on different + // nsWindowRoot, make sure the HTMLEditor for the original event target + // handles the events. + if (mEditorBase->IsHTMLEditor()) { + nsCOMPtr originalEventTargetNode = + nsINode::FromEventTargetOrNull(aEvent->GetOriginalTarget()); + + if (originalEventTargetNode && + mEditorBase != originalEventTargetNode->OwnerDoc()->GetHTMLEditor()) { + return NS_OK; + } + } + switch (internalEvent->mMessage) { // dragover and drop case eDragOver: case eDrop: { + // The editor which is registered on nsWindowRoot shouldn't handle + // drop events when it can be handled by Input or TextArea element on + // the chain. + if (aEvent->GetCurrentTarget()->IsRootWindow() && + TextControlElement::FromEventTargetOrNull( + internalEvent->GetDOMEventTarget())) { + return NS_OK; + } // aEvent should be grabbed by the caller since this is // nsIDOMEventListener method. However, our clang plugin cannot check it // if we use Event::As*Event(). So, we need to grab it by ourselves. @@ -853,9 +878,13 @@ nsresult EditorEventListener::DragOverOrDrop(DragEvent* aDragEvent) { } aDragEvent->PreventDefault(); + + WidgetDragEvent* asWidgetEvent = aDragEvent->WidgetEventPtr()->AsDragEvent(); + asWidgetEvent->UpdateDefaultPreventedOnContent(asWidgetEvent->mTarget); + aDragEvent->StopImmediatePropagation(); - if (aDragEvent->WidgetEventPtr()->mMessage == eDrop) { + if (asWidgetEvent->mMessage == eDrop) { RefPtr editorBase = mEditorBase; nsresult rv = editorBase->HandleDropEvent(aDragEvent); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), @@ -863,7 +892,7 @@ nsresult EditorEventListener::DragOverOrDrop(DragEvent* aDragEvent) { return rv; } - MOZ_ASSERT(aDragEvent->WidgetEventPtr()->mMessage == eDragOver); + MOZ_ASSERT(asWidgetEvent->mMessage == eDragOver); // If we handle the dragged item, we need to adjust drop effect here // because once DataTransfer is retrieved, DragEvent has initialized it diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 48aaa84bb605..4f816c2a48e9 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -54,6 +54,8 @@ #include "mozilla/dom/HTMLAnchorElement.h" #include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/HTMLBRElement.h" +#include "mozilla/dom/HTMLButtonElement.h" +#include "mozilla/dom/HTMLSummaryElement.h" #include "mozilla/dom/NameSpaceConstants.h" #include "mozilla/dom/Selection.h" @@ -6890,7 +6892,19 @@ EventTarget* HTMLEditor::GetDOMEventTarget() const { // whether Init() was ever called. So we need to get the document // ourselves, if it exists. MOZ_ASSERT(IsInitialized(), "The HTMLEditor has not been initialized yet"); - return GetDocument(); + Document* doc = GetDocument(); + if (!doc) { + return nullptr; + } + + // Register the EditorEventListener to the parent of window. + // + // The advantage of this approach is HTMLEditor can still + // receive events when shadow dom is involved. + if (nsPIDOMWindowOuter* win = doc->GetWindow()) { + return win->GetParentTarget(); + } + return nullptr; } bool HTMLEditor::ShouldReplaceRootElement() const { @@ -7049,6 +7063,20 @@ bool HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const { } } + // Space event for