Bug 1247796. Use keyboardFocusIndicatorColor for ActiveBorder system color keyword...
[gecko.git] / editor / libeditor / nsHTMLObjectResizer.cpp
bloba78cf3558a3b648b012dee2204ba79b00e7dd35e
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/. */
6 #include "nsHTMLObjectResizer.h"
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/LookAndFeel.h"
10 #include "mozilla/MathAlgorithms.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/mozalloc.h"
13 #include "nsAString.h"
14 #include "nsAlgorithm.h"
15 #include "nsAutoPtr.h"
16 #include "nsCOMPtr.h"
17 #include "nsDebug.h"
18 #include "nsEditorUtils.h"
19 #include "nsError.h"
20 #include "nsGkAtoms.h"
21 #include "nsHTMLCSSUtils.h"
22 #include "nsHTMLEditUtils.h"
23 #include "nsHTMLEditor.h"
24 #include "nsIAtom.h"
25 #include "nsIContent.h"
26 #include "nsID.h"
27 #include "nsIDOMDocument.h"
28 #include "nsIDOMElement.h"
29 #include "nsIDOMEvent.h"
30 #include "nsIDOMEventTarget.h"
31 #include "nsIDOMMouseEvent.h"
32 #include "nsIDOMNode.h"
33 #include "nsIDOMText.h"
34 #include "nsIDocument.h"
35 #include "nsIEditor.h"
36 #include "nsIHTMLEditor.h"
37 #include "nsIHTMLObjectResizeListener.h"
38 #include "nsIHTMLObjectResizer.h"
39 #include "nsIPresShell.h"
40 #include "nsISupportsUtils.h"
41 #include "nsPIDOMWindow.h"
42 #include "nsReadableUtils.h"
43 #include "nsString.h"
44 #include "nsStringFwd.h"
45 #include "nsSubstringTuple.h"
46 #include "nscore.h"
47 #include <algorithm>
49 class nsISelection;
51 using namespace mozilla;
52 using namespace mozilla::dom;
54 class nsHTMLEditUtils;
56 // ==================================================================
57 // DocumentResizeEventListener
58 // ==================================================================
59 NS_IMPL_ISUPPORTS(DocumentResizeEventListener, nsIDOMEventListener)
61 DocumentResizeEventListener::DocumentResizeEventListener(nsIHTMLEditor * aEditor)
63 mEditor = do_GetWeakReference(aEditor);
66 DocumentResizeEventListener::~DocumentResizeEventListener()
70 NS_IMETHODIMP
71 DocumentResizeEventListener::HandleEvent(nsIDOMEvent* aMouseEvent)
73 nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryReferent(mEditor);
74 if (objectResizer)
75 return objectResizer->RefreshResizers();
76 return NS_OK;
79 // ==================================================================
80 // ResizerSelectionListener
81 // ==================================================================
83 NS_IMPL_ISUPPORTS(ResizerSelectionListener, nsISelectionListener)
85 ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor * aEditor)
87 mEditor = do_GetWeakReference(aEditor);
90 ResizerSelectionListener::~ResizerSelectionListener()
94 NS_IMETHODIMP
95 ResizerSelectionListener::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aSelection, int16_t aReason)
97 if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
98 nsISelectionListener::KEYPRESS_REASON |
99 nsISelectionListener::SELECTALL_REASON)) && aSelection)
101 // the selection changed and we need to check if we have to
102 // hide and/or redisplay resizing handles
103 nsCOMPtr<nsIHTMLEditor> editor = do_QueryReferent(mEditor);
104 if (editor)
105 editor->CheckSelectionStateForAnonymousButtons(aSelection);
108 return NS_OK;
111 // ==================================================================
112 // ResizerMouseMotionListener
113 // ==================================================================
115 NS_IMPL_ISUPPORTS(ResizerMouseMotionListener, nsIDOMEventListener)
117 ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor * aEditor)
119 mEditor = do_GetWeakReference(aEditor);
122 ResizerMouseMotionListener::~ResizerMouseMotionListener()
126 NS_IMETHODIMP
127 ResizerMouseMotionListener::HandleEvent(nsIDOMEvent* aMouseEvent)
129 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
130 if (!mouseEvent) {
131 //non-ui event passed in. bad things.
132 return NS_OK;
135 // Don't do anything special if not an HTML object resizer editor
136 nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryReferent(mEditor);
137 if (objectResizer)
139 // check if we have to redisplay a resizing shadow
140 objectResizer->MouseMove(aMouseEvent);
143 return NS_OK;
146 // ==================================================================
147 // nsHTMLEditor
148 // ==================================================================
150 already_AddRefed<Element>
151 nsHTMLEditor::CreateResizer(int16_t aLocation, nsIDOMNode* aParentNode)
153 nsCOMPtr<nsIDOMElement> retDOM;
154 nsresult res = CreateAnonymousElement(NS_LITERAL_STRING("span"),
155 aParentNode,
156 NS_LITERAL_STRING("mozResizer"),
157 false,
158 getter_AddRefs(retDOM));
160 NS_ENSURE_SUCCESS(res, nullptr);
161 NS_ENSURE_TRUE(retDOM, nullptr);
163 // add the mouse listener so we can detect a click on a resizer
164 nsCOMPtr<nsIDOMEventTarget> evtTarget = do_QueryInterface(retDOM);
165 evtTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mEventListener,
166 true);
168 nsAutoString locationStr;
169 switch (aLocation) {
170 case nsIHTMLObjectResizer::eTopLeft:
171 locationStr = kTopLeft;
172 break;
173 case nsIHTMLObjectResizer::eTop:
174 locationStr = kTop;
175 break;
176 case nsIHTMLObjectResizer::eTopRight:
177 locationStr = kTopRight;
178 break;
180 case nsIHTMLObjectResizer::eLeft:
181 locationStr = kLeft;
182 break;
183 case nsIHTMLObjectResizer::eRight:
184 locationStr = kRight;
185 break;
187 case nsIHTMLObjectResizer::eBottomLeft:
188 locationStr = kBottomLeft;
189 break;
190 case nsIHTMLObjectResizer::eBottom:
191 locationStr = kBottom;
192 break;
193 case nsIHTMLObjectResizer::eBottomRight:
194 locationStr = kBottomRight;
195 break;
198 nsCOMPtr<Element> ret = do_QueryInterface(retDOM);
199 res = ret->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation, locationStr,
200 true);
201 NS_ENSURE_SUCCESS(res, nullptr);
202 return ret.forget();
205 already_AddRefed<Element>
206 nsHTMLEditor::CreateShadow(nsIDOMNode* aParentNode,
207 nsIDOMElement* aOriginalObject)
209 // let's create an image through the element factory
210 nsAutoString name;
211 if (nsHTMLEditUtils::IsImage(aOriginalObject))
212 name.AssignLiteral("img");
213 else
214 name.AssignLiteral("span");
215 nsCOMPtr<nsIDOMElement> retDOM;
216 CreateAnonymousElement(name, aParentNode,
217 NS_LITERAL_STRING("mozResizingShadow"), true,
218 getter_AddRefs(retDOM));
220 NS_ENSURE_TRUE(retDOM, nullptr);
222 nsCOMPtr<Element> ret = do_QueryInterface(retDOM);
223 return ret.forget();
226 already_AddRefed<Element>
227 nsHTMLEditor::CreateResizingInfo(nsIDOMNode* aParentNode)
229 // let's create an info box through the element factory
230 nsCOMPtr<nsIDOMElement> retDOM;
231 CreateAnonymousElement(NS_LITERAL_STRING("span"), aParentNode,
232 NS_LITERAL_STRING("mozResizingInfo"), true,
233 getter_AddRefs(retDOM));
235 nsCOMPtr<Element> ret = do_QueryInterface(retDOM);
236 return ret.forget();
239 nsresult
240 nsHTMLEditor::SetAllResizersPosition()
242 NS_ENSURE_TRUE(mTopLeftHandle, NS_ERROR_FAILURE);
244 int32_t x = mResizedObjectX;
245 int32_t y = mResizedObjectY;
246 int32_t w = mResizedObjectWidth;
247 int32_t h = mResizedObjectHeight;
249 // now let's place all the resizers around the image
251 // get the size of resizers
252 nsAutoString value;
253 float resizerWidth, resizerHeight;
254 nsCOMPtr<nsIAtom> dummyUnit;
255 mHTMLCSSUtils->GetComputedProperty(*mTopLeftHandle, *nsGkAtoms::width,
256 value);
257 mHTMLCSSUtils->ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit));
258 mHTMLCSSUtils->GetComputedProperty(*mTopLeftHandle, *nsGkAtoms::height,
259 value);
260 mHTMLCSSUtils->ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit));
262 int32_t rw = (int32_t)((resizerWidth + 1) / 2);
263 int32_t rh = (int32_t)((resizerHeight+ 1) / 2);
265 SetAnonymousElementPosition(x-rw, y-rh, static_cast<nsIDOMElement*>(GetAsDOMNode(mTopLeftHandle)));
266 SetAnonymousElementPosition(x+w/2-rw, y-rh, static_cast<nsIDOMElement*>(GetAsDOMNode(mTopHandle)));
267 SetAnonymousElementPosition(x+w-rw-1, y-rh, static_cast<nsIDOMElement*>(GetAsDOMNode(mTopRightHandle)));
269 SetAnonymousElementPosition(x-rw, y+h/2-rh, static_cast<nsIDOMElement*>(GetAsDOMNode(mLeftHandle)));
270 SetAnonymousElementPosition(x+w-rw-1, y+h/2-rh, static_cast<nsIDOMElement*>(GetAsDOMNode(mRightHandle)));
272 SetAnonymousElementPosition(x-rw, y+h-rh-1, static_cast<nsIDOMElement*>(GetAsDOMNode(mBottomLeftHandle)));
273 SetAnonymousElementPosition(x+w/2-rw, y+h-rh-1, static_cast<nsIDOMElement*>(GetAsDOMNode(mBottomHandle)));
274 SetAnonymousElementPosition(x+w-rw-1, y+h-rh-1, static_cast<nsIDOMElement*>(GetAsDOMNode(mBottomRightHandle)));
276 return NS_OK;
279 NS_IMETHODIMP
280 nsHTMLEditor::RefreshResizers()
282 // nothing to do if resizers are not displayed...
283 NS_ENSURE_TRUE(mResizedObject, NS_OK);
285 nsresult res = GetPositionAndDimensions(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)),
286 mResizedObjectX,
287 mResizedObjectY,
288 mResizedObjectWidth,
289 mResizedObjectHeight,
290 mResizedObjectBorderLeft,
291 mResizedObjectBorderTop,
292 mResizedObjectMarginLeft,
293 mResizedObjectMarginTop);
295 NS_ENSURE_SUCCESS(res, res);
296 res = SetAllResizersPosition();
297 NS_ENSURE_SUCCESS(res, res);
298 return SetShadowPosition(mResizingShadow, mResizedObject,
299 mResizedObjectX, mResizedObjectY);
302 NS_IMETHODIMP
303 nsHTMLEditor::ShowResizers(nsIDOMElement *aResizedElement)
305 nsresult res = ShowResizersInner(aResizedElement);
306 if (NS_FAILED(res))
307 HideResizers();
308 return res;
311 nsresult
312 nsHTMLEditor::ShowResizersInner(nsIDOMElement *aResizedElement)
314 NS_ENSURE_ARG_POINTER(aResizedElement);
315 nsresult res;
317 nsCOMPtr<nsIDOMNode> parentNode;
318 res = aResizedElement->GetParentNode(getter_AddRefs(parentNode));
319 NS_ENSURE_SUCCESS(res, res);
321 if (mResizedObject) {
322 NS_ERROR("call HideResizers first");
323 return NS_ERROR_UNEXPECTED;
325 mResizedObject = do_QueryInterface(aResizedElement);
326 NS_ENSURE_STATE(mResizedObject);
328 // The resizers and the shadow will be anonymous siblings of the element.
329 mTopLeftHandle = CreateResizer(nsIHTMLObjectResizer::eTopLeft, parentNode);
330 NS_ENSURE_TRUE(mTopLeftHandle, NS_ERROR_FAILURE);
331 mTopHandle = CreateResizer(nsIHTMLObjectResizer::eTop, parentNode);
332 NS_ENSURE_TRUE(mTopHandle, NS_ERROR_FAILURE);
333 mTopRightHandle = CreateResizer(nsIHTMLObjectResizer::eTopRight, parentNode);
334 NS_ENSURE_TRUE(mTopRightHandle, NS_ERROR_FAILURE);
336 mLeftHandle = CreateResizer(nsIHTMLObjectResizer::eLeft, parentNode);
337 NS_ENSURE_TRUE(mLeftHandle, NS_ERROR_FAILURE);
338 mRightHandle = CreateResizer(nsIHTMLObjectResizer::eRight, parentNode);
339 NS_ENSURE_TRUE(mRightHandle, NS_ERROR_FAILURE);
341 mBottomLeftHandle = CreateResizer(nsIHTMLObjectResizer::eBottomLeft, parentNode);
342 NS_ENSURE_TRUE(mBottomLeftHandle, NS_ERROR_FAILURE);
343 mBottomHandle = CreateResizer(nsIHTMLObjectResizer::eBottom, parentNode);
344 NS_ENSURE_TRUE(mBottomHandle, NS_ERROR_FAILURE);
345 mBottomRightHandle = CreateResizer(nsIHTMLObjectResizer::eBottomRight, parentNode);
346 NS_ENSURE_TRUE(mBottomRightHandle, NS_ERROR_FAILURE);
348 res = GetPositionAndDimensions(aResizedElement,
349 mResizedObjectX,
350 mResizedObjectY,
351 mResizedObjectWidth,
352 mResizedObjectHeight,
353 mResizedObjectBorderLeft,
354 mResizedObjectBorderTop,
355 mResizedObjectMarginLeft,
356 mResizedObjectMarginTop);
357 NS_ENSURE_SUCCESS(res, res);
359 // and let's set their absolute positions in the document
360 res = SetAllResizersPosition();
361 NS_ENSURE_SUCCESS(res, res);
363 // now, let's create the resizing shadow
364 mResizingShadow = CreateShadow(parentNode, aResizedElement);
365 NS_ENSURE_TRUE(mResizingShadow, NS_ERROR_FAILURE);
366 // and set its position
367 res = SetShadowPosition(mResizingShadow, mResizedObject,
368 mResizedObjectX, mResizedObjectY);
369 NS_ENSURE_SUCCESS(res, res);
371 // and then the resizing info tooltip
372 mResizingInfo = CreateResizingInfo(parentNode);
373 NS_ENSURE_TRUE(mResizingInfo, NS_ERROR_FAILURE);
375 // and listen to the "resize" event on the window first, get the
376 // window from the document...
377 nsCOMPtr<nsIDocument> doc = GetDocument();
378 NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
380 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(doc->GetWindow());
381 if (!target) { return NS_ERROR_NULL_POINTER; }
383 mResizeEventListenerP = new DocumentResizeEventListener(this);
384 if (!mResizeEventListenerP) { return NS_ERROR_OUT_OF_MEMORY; }
385 res = target->AddEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
387 aResizedElement->SetAttribute(NS_LITERAL_STRING("_moz_resizing"), NS_LITERAL_STRING("true"));
388 return res;
391 NS_IMETHODIMP
392 nsHTMLEditor::HideResizers(void)
394 NS_ENSURE_TRUE(mResizedObject, NS_OK);
396 // get the presshell's document observer interface.
397 nsCOMPtr<nsIPresShell> ps = GetPresShell();
398 // We allow the pres shell to be null; when it is, we presume there
399 // are no document observers to notify, but we still want to
400 // UnbindFromTree.
402 nsCOMPtr<nsIContent> parentContent;
404 if (mTopLeftHandle) {
405 parentContent = mTopLeftHandle->GetParent();
408 NS_NAMED_LITERAL_STRING(mousedown, "mousedown");
410 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
411 mTopLeftHandle, parentContent, ps);
412 mTopLeftHandle = nullptr;
414 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
415 mTopHandle, parentContent, ps);
416 mTopHandle = nullptr;
418 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
419 mTopRightHandle, parentContent, ps);
420 mTopRightHandle = nullptr;
422 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
423 mLeftHandle, parentContent, ps);
424 mLeftHandle = nullptr;
426 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
427 mRightHandle, parentContent, ps);
428 mRightHandle = nullptr;
430 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
431 mBottomLeftHandle, parentContent, ps);
432 mBottomLeftHandle = nullptr;
434 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
435 mBottomHandle, parentContent, ps);
436 mBottomHandle = nullptr;
438 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
439 mBottomRightHandle, parentContent, ps);
440 mBottomRightHandle = nullptr;
442 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
443 mResizingShadow, parentContent, ps);
444 mResizingShadow = nullptr;
446 RemoveListenerAndDeleteRef(mousedown, mEventListener, true,
447 mResizingInfo, parentContent, ps);
448 mResizingInfo = nullptr;
450 if (mActivatedHandle) {
451 mActivatedHandle->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_activated,
452 true);
453 mActivatedHandle = nullptr;
456 // don't forget to remove the listeners !
458 nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
460 DebugOnly<nsresult> res;
461 if (target && mMouseMotionListenerP)
463 res = target->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
464 mMouseMotionListenerP, true);
465 NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove mouse motion listener");
467 mMouseMotionListenerP = nullptr;
469 nsCOMPtr<nsIDocument> doc = GetDocument();
470 if (!doc) { return NS_ERROR_NULL_POINTER; }
471 target = do_QueryInterface(doc->GetWindow());
472 if (!target) { return NS_ERROR_NULL_POINTER; }
474 if (mResizeEventListenerP) {
475 res = target->RemoveEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
476 NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove resize event listener");
478 mResizeEventListenerP = nullptr;
480 mResizedObject->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_resizing, true);
481 mResizedObject = nullptr;
483 return NS_OK;
486 void
487 nsHTMLEditor::HideShadowAndInfo()
489 if (mResizingShadow)
490 mResizingShadow->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
491 NS_LITERAL_STRING("hidden"), true);
492 if (mResizingInfo)
493 mResizingInfo->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
494 NS_LITERAL_STRING("hidden"), true);
497 nsresult
498 nsHTMLEditor::StartResizing(nsIDOMElement *aHandle)
500 // First notify the listeners if any
501 for (auto& listener : mObjectResizeEventListeners) {
502 listener->OnStartResizing(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)));
505 mIsResizing = true;
506 mActivatedHandle = do_QueryInterface(aHandle);
507 NS_ENSURE_STATE(mActivatedHandle || !aHandle);
508 mActivatedHandle->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_activated,
509 NS_LITERAL_STRING("true"), true);
511 // do we want to preserve ratio or not?
512 bool preserveRatio = nsHTMLEditUtils::IsImage(mResizedObject) &&
513 Preferences::GetBool("editor.resizing.preserve_ratio", true);
515 // the way we change the position/size of the shadow depends on
516 // the handle
517 nsAutoString locationStr;
518 aHandle->GetAttribute(NS_LITERAL_STRING("anonlocation"), locationStr);
519 if (locationStr.Equals(kTopLeft)) {
520 SetResizeIncrements(1, 1, -1, -1, preserveRatio);
522 else if (locationStr.Equals(kTop)) {
523 SetResizeIncrements(0, 1, 0, -1, false);
525 else if (locationStr.Equals(kTopRight)) {
526 SetResizeIncrements(0, 1, 1, -1, preserveRatio);
528 else if (locationStr.Equals(kLeft)) {
529 SetResizeIncrements(1, 0, -1, 0, false);
531 else if (locationStr.Equals(kRight)) {
532 SetResizeIncrements(0, 0, 1, 0, false);
534 else if (locationStr.Equals(kBottomLeft)) {
535 SetResizeIncrements(1, 0, -1, 1, preserveRatio);
537 else if (locationStr.Equals(kBottom)) {
538 SetResizeIncrements(0, 0, 0, 1, false);
540 else if (locationStr.Equals(kBottomRight)) {
541 SetResizeIncrements(0, 0, 1, 1, preserveRatio);
544 // make the shadow appear
545 mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
547 // position it
548 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::width,
549 mResizedObjectWidth);
550 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::height,
551 mResizedObjectHeight);
553 // add a mouse move listener to the editor
554 nsresult result = NS_OK;
555 if (!mMouseMotionListenerP) {
556 mMouseMotionListenerP = new ResizerMouseMotionListener(this);
557 if (!mMouseMotionListenerP) {
558 return NS_ERROR_OUT_OF_MEMORY;
561 nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
562 NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
564 result = target->AddEventListener(NS_LITERAL_STRING("mousemove"),
565 mMouseMotionListenerP, true);
566 NS_ASSERTION(NS_SUCCEEDED(result),
567 "failed to register mouse motion listener");
569 return result;
573 NS_IMETHODIMP
574 nsHTMLEditor::MouseDown(int32_t aClientX, int32_t aClientY,
575 nsIDOMElement *aTarget, nsIDOMEvent* aEvent)
577 bool anonElement = false;
578 if (aTarget && NS_SUCCEEDED(aTarget->HasAttribute(NS_LITERAL_STRING("_moz_anonclass"), &anonElement)))
579 // we caught a click on an anonymous element
580 if (anonElement) {
581 nsAutoString anonclass;
582 nsresult res = aTarget->GetAttribute(NS_LITERAL_STRING("_moz_anonclass"), anonclass);
583 NS_ENSURE_SUCCESS(res, res);
584 if (anonclass.EqualsLiteral("mozResizer")) {
585 // and that element is a resizer, let's start resizing!
586 aEvent->PreventDefault();
588 mOriginalX = aClientX;
589 mOriginalY = aClientY;
590 return StartResizing(aTarget);
592 if (anonclass.EqualsLiteral("mozGrabber")) {
593 // and that element is a grabber, let's start moving the element!
594 mOriginalX = aClientX;
595 mOriginalY = aClientY;
596 return GrabberClicked();
599 return NS_OK;
602 NS_IMETHODIMP
603 nsHTMLEditor::MouseUp(int32_t aClientX, int32_t aClientY,
604 nsIDOMElement *aTarget)
606 if (mIsResizing) {
607 // we are resizing and release the mouse button, so let's
608 // end the resizing process
609 mIsResizing = false;
610 HideShadowAndInfo();
611 SetFinalSize(aClientX, aClientY);
613 else if (mIsMoving || mGrabberClicked) {
614 if (mIsMoving) {
615 mPositioningShadow->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
616 NS_LITERAL_STRING("hidden"), true);
617 SetFinalPosition(aClientX, aClientY);
619 if (mGrabberClicked) {
620 EndMoving();
623 return NS_OK;
627 void
628 nsHTMLEditor::SetResizeIncrements(int32_t aX, int32_t aY,
629 int32_t aW, int32_t aH,
630 bool aPreserveRatio)
632 mXIncrementFactor = aX;
633 mYIncrementFactor = aY;
634 mWidthIncrementFactor = aW;
635 mHeightIncrementFactor = aH;
636 mPreserveRatio = aPreserveRatio;
639 nsresult
640 nsHTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW, int32_t aH)
642 nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
644 // Determine the position of the resizing info box based upon the new
645 // position and size of the element (aX, aY, aW, aH), and which
646 // resizer is the "activated handle". For example, place the resizing
647 // info box at the bottom-right corner of the new element, if the element
648 // is being resized by the bottom-right resizer.
649 int32_t infoXPosition;
650 int32_t infoYPosition;
652 if (mActivatedHandle == mTopLeftHandle ||
653 mActivatedHandle == mLeftHandle ||
654 mActivatedHandle == mBottomLeftHandle)
655 infoXPosition = aX;
656 else if (mActivatedHandle == mTopHandle ||
657 mActivatedHandle == mBottomHandle)
658 infoXPosition = aX + (aW / 2);
659 else
660 // should only occur when mActivatedHandle is one of the 3 right-side
661 // handles, but this is a reasonable default if it isn't any of them (?)
662 infoXPosition = aX + aW;
664 if (mActivatedHandle == mTopLeftHandle ||
665 mActivatedHandle == mTopHandle ||
666 mActivatedHandle == mTopRightHandle)
667 infoYPosition = aY;
668 else if (mActivatedHandle == mLeftHandle ||
669 mActivatedHandle == mRightHandle)
670 infoYPosition = aY + (aH / 2);
671 else
672 // should only occur when mActivatedHandle is one of the 3 bottom-side
673 // handles, but this is a reasonable default if it isn't any of them (?)
674 infoYPosition = aY + aH;
676 // Offset info box by 20 so it's not directly under the mouse cursor.
677 const int mouseCursorOffset = 20;
678 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingInfo, *nsGkAtoms::left,
679 infoXPosition + mouseCursorOffset);
680 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingInfo, *nsGkAtoms::top,
681 infoYPosition + mouseCursorOffset);
683 nsCOMPtr<nsIContent> textInfo = mResizingInfo->GetFirstChild();
684 ErrorResult rv;
685 if (textInfo) {
686 mResizingInfo->RemoveChild(*textInfo, rv);
687 NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
688 textInfo = nullptr;
691 nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr;
692 widthStr.AppendInt(aW);
693 heightStr.AppendInt(aH);
694 int32_t diffWidth = aW - mResizedObjectWidth;
695 int32_t diffHeight = aH - mResizedObjectHeight;
696 if (diffWidth > 0)
697 diffWidthStr.Assign('+');
698 if (diffHeight > 0)
699 diffHeightStr.Assign('+');
700 diffWidthStr.AppendInt(diffWidth);
701 diffHeightStr.AppendInt(diffHeight);
703 nsAutoString info(widthStr + NS_LITERAL_STRING(" x ") + heightStr +
704 NS_LITERAL_STRING(" (") + diffWidthStr +
705 NS_LITERAL_STRING(", ") + diffHeightStr +
706 NS_LITERAL_STRING(")"));
708 nsCOMPtr<nsIDOMText> nodeAsText;
709 nsresult res = domdoc->CreateTextNode(info, getter_AddRefs(nodeAsText));
710 NS_ENSURE_SUCCESS(res, res);
711 textInfo = do_QueryInterface(nodeAsText);
712 mResizingInfo->AppendChild(*textInfo, rv);
713 NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
715 res = mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
717 return res;
720 nsresult
721 nsHTMLEditor::SetShadowPosition(Element* aShadow,
722 Element* aOriginalObject,
723 int32_t aOriginalObjectX,
724 int32_t aOriginalObjectY)
726 SetAnonymousElementPosition(aOriginalObjectX, aOriginalObjectY, static_cast<nsIDOMElement*>(GetAsDOMNode(aShadow)));
728 if (nsHTMLEditUtils::IsImage(aOriginalObject)) {
729 nsAutoString imageSource;
730 aOriginalObject->GetAttr(kNameSpaceID_None, nsGkAtoms::src, imageSource);
731 nsresult res = aShadow->SetAttr(kNameSpaceID_None, nsGkAtoms::src,
732 imageSource, true);
733 NS_ENSURE_SUCCESS(res, res);
735 return NS_OK;
738 int32_t
739 nsHTMLEditor::GetNewResizingIncrement(int32_t aX, int32_t aY, int32_t aID)
741 int32_t result = 0;
742 if (!mPreserveRatio) {
743 switch (aID) {
744 case kX:
745 case kWidth:
746 result = aX - mOriginalX;
747 break;
748 case kY:
749 case kHeight:
750 result = aY - mOriginalY;
751 break;
753 return result;
756 int32_t xi = (aX - mOriginalX) * mWidthIncrementFactor;
757 int32_t yi = (aY - mOriginalY) * mHeightIncrementFactor;
758 float objectSizeRatio =
759 ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight);
760 result = (xi > yi) ? xi : yi;
761 switch (aID) {
762 case kX:
763 case kWidth:
764 if (result == yi)
765 result = (int32_t) (((float) result) * objectSizeRatio);
766 result = (int32_t) (((float) result) * mWidthIncrementFactor);
767 break;
768 case kY:
769 case kHeight:
770 if (result == xi)
771 result = (int32_t) (((float) result) / objectSizeRatio);
772 result = (int32_t) (((float) result) * mHeightIncrementFactor);
773 break;
775 return result;
778 int32_t
779 nsHTMLEditor::GetNewResizingX(int32_t aX, int32_t aY)
781 int32_t resized = mResizedObjectX +
782 GetNewResizingIncrement(aX, aY, kX) * mXIncrementFactor;
783 int32_t max = mResizedObjectX + mResizedObjectWidth;
784 return std::min(resized, max);
787 int32_t
788 nsHTMLEditor::GetNewResizingY(int32_t aX, int32_t aY)
790 int32_t resized = mResizedObjectY +
791 GetNewResizingIncrement(aX, aY, kY) * mYIncrementFactor;
792 int32_t max = mResizedObjectY + mResizedObjectHeight;
793 return std::min(resized, max);
796 int32_t
797 nsHTMLEditor::GetNewResizingWidth(int32_t aX, int32_t aY)
799 int32_t resized = mResizedObjectWidth +
800 GetNewResizingIncrement(aX, aY, kWidth) *
801 mWidthIncrementFactor;
802 return std::max(resized, 1);
805 int32_t
806 nsHTMLEditor::GetNewResizingHeight(int32_t aX, int32_t aY)
808 int32_t resized = mResizedObjectHeight +
809 GetNewResizingIncrement(aX, aY, kHeight) *
810 mHeightIncrementFactor;
811 return std::max(resized, 1);
815 NS_IMETHODIMP
816 nsHTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent)
818 NS_NAMED_LITERAL_STRING(leftStr, "left");
819 NS_NAMED_LITERAL_STRING(topStr, "top");
821 if (mIsResizing) {
822 // we are resizing and the mouse pointer's position has changed
823 // we have to resdisplay the shadow
824 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
825 int32_t clientX, clientY;
826 mouseEvent->GetClientX(&clientX);
827 mouseEvent->GetClientY(&clientY);
829 int32_t newX = GetNewResizingX(clientX, clientY);
830 int32_t newY = GetNewResizingY(clientX, clientY);
831 int32_t newWidth = GetNewResizingWidth(clientX, clientY);
832 int32_t newHeight = GetNewResizingHeight(clientX, clientY);
834 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::left,
835 newX);
836 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::top,
837 newY);
838 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::width,
839 newWidth);
840 mHTMLCSSUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::height,
841 newHeight);
843 return SetResizingInfoPosition(newX, newY, newWidth, newHeight);
846 if (mGrabberClicked) {
847 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
848 int32_t clientX, clientY;
849 mouseEvent->GetClientX(&clientX);
850 mouseEvent->GetClientY(&clientY);
852 int32_t xThreshold =
853 LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 1);
854 int32_t yThreshold =
855 LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 1);
857 if (DeprecatedAbs(clientX - mOriginalX) * 2 >= xThreshold ||
858 DeprecatedAbs(clientY - mOriginalY) * 2 >= yThreshold) {
859 mGrabberClicked = false;
860 StartMoving(nullptr);
863 if (mIsMoving) {
864 nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
865 int32_t clientX, clientY;
866 mouseEvent->GetClientX(&clientX);
867 mouseEvent->GetClientY(&clientY);
869 int32_t newX = mPositionedObjectX + clientX - mOriginalX;
870 int32_t newY = mPositionedObjectY + clientY - mOriginalY;
872 SnapToGrid(newX, newY);
874 mHTMLCSSUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::left,
875 newX);
876 mHTMLCSSUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::top,
877 newY);
879 return NS_OK;
882 void
883 nsHTMLEditor::SetFinalSize(int32_t aX, int32_t aY)
885 if (!mResizedObject) {
886 // paranoia
887 return;
890 if (mActivatedHandle) {
891 mActivatedHandle->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_activated, true);
892 mActivatedHandle = nullptr;
895 // we have now to set the new width and height of the resized object
896 // we don't set the x and y position because we don't control that in
897 // a normal HTML layout
898 int32_t left = GetNewResizingX(aX, aY);
899 int32_t top = GetNewResizingY(aX, aY);
900 int32_t width = GetNewResizingWidth(aX, aY);
901 int32_t height = GetNewResizingHeight(aX, aY);
902 bool setWidth = !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth);
903 bool setHeight = !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight);
905 int32_t x, y;
906 x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft+mResizedObjectMarginLeft : 0);
907 y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop+mResizedObjectMarginTop : 0);
909 // we want one transaction only from a user's point of view
910 nsAutoEditBatch batchIt(this);
912 NS_NAMED_LITERAL_STRING(widthStr, "width");
913 NS_NAMED_LITERAL_STRING(heightStr, "height");
915 nsCOMPtr<Element> resizedObject = do_QueryInterface(mResizedObject);
916 NS_ENSURE_TRUE(resizedObject, );
917 if (mResizedObjectIsAbsolutelyPositioned) {
918 if (setHeight)
919 mHTMLCSSUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::top, y);
920 if (setWidth)
921 mHTMLCSSUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::left, x);
923 if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) {
924 if (setWidth && mResizedObject->HasAttr(kNameSpaceID_None, nsGkAtoms::width)) {
925 RemoveAttribute(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)), widthStr);
928 if (setHeight && mResizedObject->HasAttr(kNameSpaceID_None,
929 nsGkAtoms::height)) {
930 RemoveAttribute(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)), heightStr);
933 if (setWidth)
934 mHTMLCSSUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::width,
935 width);
936 if (setHeight)
937 mHTMLCSSUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::height,
938 height);
940 else {
941 // we use HTML size and remove all equivalent CSS properties
943 // we set the CSS width and height to remove it later,
944 // triggering an immediate reflow; otherwise, we have problems
945 // with asynchronous reflow
946 if (setWidth)
947 mHTMLCSSUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::width,
948 width);
949 if (setHeight)
950 mHTMLCSSUtils->SetCSSPropertyPixels(*resizedObject, *nsGkAtoms::height,
951 height);
953 if (setWidth) {
954 nsAutoString w;
955 w.AppendInt(width);
956 SetAttribute(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)), widthStr, w);
958 if (setHeight) {
959 nsAutoString h;
960 h.AppendInt(height);
961 SetAttribute(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)), heightStr, h);
964 if (setWidth)
965 mHTMLCSSUtils->RemoveCSSProperty(*resizedObject, *nsGkAtoms::width,
966 EmptyString());
967 if (setHeight)
968 mHTMLCSSUtils->RemoveCSSProperty(*resizedObject, *nsGkAtoms::height,
969 EmptyString());
971 // finally notify the listeners if any
972 for (auto& listener : mObjectResizeEventListeners) {
973 listener->OnEndResizing(static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject)),
974 mResizedObjectWidth, mResizedObjectHeight, width,
975 height);
978 // keep track of that size
979 mResizedObjectWidth = width;
980 mResizedObjectHeight = height;
982 RefreshResizers();
985 NS_IMETHODIMP
986 nsHTMLEditor::GetResizedObject(nsIDOMElement * *aResizedObject)
988 nsCOMPtr<nsIDOMElement> ret = static_cast<nsIDOMElement*>(GetAsDOMNode(mResizedObject));
989 ret.forget(aResizedObject);
990 return NS_OK;
993 NS_IMETHODIMP
994 nsHTMLEditor::GetObjectResizingEnabled(bool *aIsObjectResizingEnabled)
996 *aIsObjectResizingEnabled = mIsObjectResizingEnabled;
997 return NS_OK;
1000 NS_IMETHODIMP
1001 nsHTMLEditor::SetObjectResizingEnabled(bool aObjectResizingEnabled)
1003 mIsObjectResizingEnabled = aObjectResizingEnabled;
1004 return NS_OK;
1007 NS_IMETHODIMP
1008 nsHTMLEditor::AddObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener)
1010 NS_ENSURE_ARG_POINTER(aListener);
1011 if (mObjectResizeEventListeners.Contains(aListener)) {
1012 /* listener already registered */
1013 NS_ASSERTION(false,
1014 "trying to register an already registered object resize event listener");
1015 return NS_OK;
1017 mObjectResizeEventListeners.AppendElement(*aListener);
1018 return NS_OK;
1021 NS_IMETHODIMP
1022 nsHTMLEditor::RemoveObjectResizeEventListener(nsIHTMLObjectResizeListener * aListener)
1024 NS_ENSURE_ARG_POINTER(aListener);
1025 if (!mObjectResizeEventListeners.Contains(aListener)) {
1026 /* listener was not registered */
1027 NS_ASSERTION(false,
1028 "trying to remove an object resize event listener that was not already registered");
1029 return NS_OK;
1031 mObjectResizeEventListeners.RemoveElement(aListener);
1032 return NS_OK;