Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "HTMLEditorEventListener.h"
7 :
8 : #include "HTMLEditUtils.h"
9 : #include "mozilla/HTMLEditor.h"
10 : #include "mozilla/MouseEvents.h"
11 : #include "mozilla/dom/Event.h"
12 : #include "mozilla/dom/Selection.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsDebug.h"
15 : #include "nsError.h"
16 : #include "nsIDOMElement.h"
17 : #include "nsIDOMEvent.h"
18 : #include "nsIDOMEventTarget.h"
19 : #include "nsIDOMMouseEvent.h"
20 : #include "nsIDOMNode.h"
21 : #include "nsIHTMLInlineTableEditor.h"
22 : #include "nsIHTMLObjectResizer.h"
23 : #include "nsISupportsImpl.h"
24 : #include "nsLiteralString.h"
25 : #include "nsQueryObject.h"
26 : #include "nsRange.h"
27 :
28 : namespace mozilla {
29 :
30 : using namespace dom;
31 :
32 : nsresult
33 0 : HTMLEditorEventListener::Connect(EditorBase* aEditorBase)
34 : {
35 0 : if (NS_WARN_IF(!aEditorBase)) {
36 0 : return NS_ERROR_INVALID_ARG;
37 : }
38 : // Guarantee that mEditorBase is always HTMLEditor.
39 0 : HTMLEditor* htmlEditor = aEditorBase->AsHTMLEditor();
40 0 : if (NS_WARN_IF(!htmlEditor)) {
41 0 : return NS_ERROR_INVALID_ARG;
42 : }
43 0 : return EditorEventListener::Connect(htmlEditor);
44 : }
45 :
46 : nsresult
47 0 : HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
48 : {
49 0 : if (DetachedFromEditor()) {
50 0 : return NS_OK;
51 : }
52 :
53 : // FYI: We need to notify HTML editor of mouseup even if it's consumed
54 : // because HTML editor always needs to release grabbing resizer.
55 0 : HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
56 0 : MOZ_ASSERT(htmlEditor);
57 :
58 0 : nsCOMPtr<nsIDOMEventTarget> target;
59 0 : nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
60 0 : NS_ENSURE_SUCCESS(rv, rv);
61 0 : NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
62 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
63 :
64 : int32_t clientX, clientY;
65 0 : aMouseEvent->GetClientX(&clientX);
66 0 : aMouseEvent->GetClientY(&clientY);
67 0 : htmlEditor->MouseUp(clientX, clientY, element);
68 :
69 0 : return EditorEventListener::MouseUp(aMouseEvent);
70 : }
71 :
72 : nsresult
73 0 : HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
74 : {
75 0 : if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
76 0 : return NS_OK;
77 : }
78 :
79 : // Even if it's not acceptable mousedown event (i.e., when mousedown
80 : // event is fired outside of the active editing host), we need to commit
81 : // composition because it will be change the selection to the clicked
82 : // point. Then, we won't be able to commit the composition.
83 0 : if (!EnsureCommitCompoisition()) {
84 0 : return NS_OK;
85 : }
86 :
87 : WidgetMouseEvent* mousedownEvent =
88 0 : aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
89 :
90 0 : HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
91 0 : MOZ_ASSERT(htmlEditor);
92 :
93 : // Contenteditable should disregard mousedowns outside it.
94 : // IsAcceptableInputEvent() checks it for a mouse event.
95 0 : if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) {
96 0 : return EditorEventListener::MouseDown(aMouseEvent);
97 : }
98 :
99 : // Detect only "context menu" click
100 : // XXX This should be easier to do!
101 : // But eDOMEvents_contextmenu and eContextMenu is not exposed in any event
102 : // interface :-(
103 : int16_t buttonNumber;
104 0 : nsresult rv = aMouseEvent->GetButton(&buttonNumber);
105 0 : NS_ENSURE_SUCCESS(rv, rv);
106 :
107 0 : bool isContextClick = buttonNumber == 2;
108 :
109 : int32_t clickCount;
110 0 : rv = aMouseEvent->GetDetail(&clickCount);
111 0 : NS_ENSURE_SUCCESS(rv, rv);
112 :
113 0 : nsCOMPtr<nsIDOMEventTarget> target;
114 0 : rv = aMouseEvent->AsEvent()->GetExplicitOriginalTarget(getter_AddRefs(target));
115 0 : NS_ENSURE_SUCCESS(rv, rv);
116 0 : NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
117 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
118 :
119 0 : if (isContextClick || (buttonNumber == 0 && clickCount == 2)) {
120 0 : RefPtr<Selection> selection = htmlEditor->GetSelection();
121 0 : NS_ENSURE_TRUE(selection, NS_OK);
122 :
123 : // Get location of mouse within target node
124 0 : nsCOMPtr<nsIDOMNode> parent;
125 0 : rv = aMouseEvent->GetRangeParent(getter_AddRefs(parent));
126 0 : NS_ENSURE_SUCCESS(rv, rv);
127 0 : NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
128 :
129 0 : int32_t offset = 0;
130 0 : rv = aMouseEvent->GetRangeOffset(&offset);
131 0 : NS_ENSURE_SUCCESS(rv, rv);
132 :
133 : // Detect if mouse point is within current selection for context click
134 0 : bool nodeIsInSelection = false;
135 0 : if (isContextClick && !selection->Collapsed()) {
136 : int32_t rangeCount;
137 0 : rv = selection->GetRangeCount(&rangeCount);
138 0 : NS_ENSURE_SUCCESS(rv, rv);
139 :
140 0 : for (int32_t i = 0; i < rangeCount; i++) {
141 0 : RefPtr<nsRange> range = selection->GetRangeAt(i);
142 0 : if (!range) {
143 : // Don't bail yet, iterate through them all
144 0 : continue;
145 : }
146 :
147 0 : range->IsPointInRange(parent, offset, &nodeIsInSelection);
148 :
149 : // Done when we find a range that we are in
150 0 : if (nodeIsInSelection) {
151 0 : break;
152 : }
153 : }
154 : }
155 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(target);
156 0 : if (node && !nodeIsInSelection) {
157 0 : if (!element) {
158 0 : if (isContextClick) {
159 : // Set the selection to the point under the mouse cursor:
160 0 : selection->Collapse(parent, offset);
161 : } else {
162 : // Get enclosing link if in text so we can select the link
163 0 : nsCOMPtr<nsIDOMElement> linkElement;
164 0 : rv = htmlEditor->GetElementOrParentByTagName(
165 0 : NS_LITERAL_STRING("href"), node,
166 0 : getter_AddRefs(linkElement));
167 0 : NS_ENSURE_SUCCESS(rv, rv);
168 0 : if (linkElement) {
169 0 : element = linkElement;
170 : }
171 : }
172 : }
173 : // Select entire element clicked on if NOT within an existing selection
174 : // and not the entire body, or table-related elements
175 0 : if (element) {
176 0 : if (isContextClick && !HTMLEditUtils::IsImage(node)) {
177 0 : selection->Collapse(parent, offset);
178 : } else {
179 0 : htmlEditor->SelectElement(element);
180 : }
181 0 : if (DetachedFromEditor()) {
182 0 : return NS_OK;
183 : }
184 : }
185 : }
186 : // HACK !!! Context click places the caret but the context menu consumes
187 : // the event; so we need to check resizing state ourselves
188 0 : htmlEditor->CheckSelectionStateForAnonymousButtons(selection);
189 :
190 : // Prevent bubbling if we changed selection or
191 : // for all context clicks
192 0 : if (element || isContextClick) {
193 0 : aMouseEvent->AsEvent()->PreventDefault();
194 0 : return NS_OK;
195 0 : }
196 0 : } else if (!isContextClick && buttonNumber == 0 && clickCount == 1) {
197 : // if the target element is an image, we have to display resizers
198 : int32_t clientX, clientY;
199 0 : aMouseEvent->GetClientX(&clientX);
200 0 : aMouseEvent->GetClientY(&clientY);
201 0 : htmlEditor->MouseDown(clientX, clientY, element, aMouseEvent->AsEvent());
202 : }
203 :
204 0 : return EditorEventListener::MouseDown(aMouseEvent);
205 : }
206 :
207 : nsresult
208 0 : HTMLEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
209 : {
210 0 : if (NS_WARN_IF(DetachedFromEditor())) {
211 0 : return NS_OK;
212 : }
213 :
214 0 : nsCOMPtr<nsIDOMEventTarget> target;
215 0 : nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
216 0 : NS_ENSURE_SUCCESS(rv, rv);
217 0 : NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
218 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
219 :
220 0 : HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
221 0 : MOZ_ASSERT(htmlEditor);
222 0 : htmlEditor->DoInlineTableEditingAction(element);
223 :
224 0 : return EditorEventListener::MouseClick(aMouseEvent);
225 : }
226 :
227 : } // namespace mozilla
|