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"
18 #include "nsEditorUtils.h"
20 #include "nsGkAtoms.h"
21 #include "nsHTMLCSSUtils.h"
22 #include "nsHTMLEditUtils.h"
23 #include "nsHTMLEditor.h"
25 #include "nsIContent.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"
44 #include "nsStringFwd.h"
45 #include "nsSubstringTuple.h"
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()
71 DocumentResizeEventListener::HandleEvent(nsIDOMEvent
* aMouseEvent
)
73 nsCOMPtr
<nsIHTMLObjectResizer
> objectResizer
= do_QueryReferent(mEditor
);
75 return objectResizer
->RefreshResizers();
79 // ==================================================================
80 // ResizerSelectionListener
81 // ==================================================================
83 NS_IMPL_ISUPPORTS(ResizerSelectionListener
, nsISelectionListener
)
85 ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor
* aEditor
)
87 mEditor
= do_GetWeakReference(aEditor
);
90 ResizerSelectionListener::~ResizerSelectionListener()
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
);
105 editor
->CheckSelectionStateForAnonymousButtons(aSelection
);
111 // ==================================================================
112 // ResizerMouseMotionListener
113 // ==================================================================
115 NS_IMPL_ISUPPORTS(ResizerMouseMotionListener
, nsIDOMEventListener
)
117 ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor
* aEditor
)
119 mEditor
= do_GetWeakReference(aEditor
);
122 ResizerMouseMotionListener::~ResizerMouseMotionListener()
127 ResizerMouseMotionListener::HandleEvent(nsIDOMEvent
* aMouseEvent
)
129 nsCOMPtr
<nsIDOMMouseEvent
> mouseEvent ( do_QueryInterface(aMouseEvent
) );
131 //non-ui event passed in. bad things.
135 // Don't do anything special if not an HTML object resizer editor
136 nsCOMPtr
<nsIHTMLObjectResizer
> objectResizer
= do_QueryReferent(mEditor
);
139 // check if we have to redisplay a resizing shadow
140 objectResizer
->MouseMove(aMouseEvent
);
146 // ==================================================================
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"),
156 NS_LITERAL_STRING("mozResizer"),
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
,
168 nsAutoString locationStr
;
170 case nsIHTMLObjectResizer::eTopLeft
:
171 locationStr
= kTopLeft
;
173 case nsIHTMLObjectResizer::eTop
:
176 case nsIHTMLObjectResizer::eTopRight
:
177 locationStr
= kTopRight
;
180 case nsIHTMLObjectResizer::eLeft
:
183 case nsIHTMLObjectResizer::eRight
:
184 locationStr
= kRight
;
187 case nsIHTMLObjectResizer::eBottomLeft
:
188 locationStr
= kBottomLeft
;
190 case nsIHTMLObjectResizer::eBottom
:
191 locationStr
= kBottom
;
193 case nsIHTMLObjectResizer::eBottomRight
:
194 locationStr
= kBottomRight
;
198 nsCOMPtr
<Element
> ret
= do_QueryInterface(retDOM
);
199 res
= ret
->SetAttr(kNameSpaceID_None
, nsGkAtoms::anonlocation
, locationStr
,
201 NS_ENSURE_SUCCESS(res
, nullptr);
205 already_AddRefed
<Element
>
206 nsHTMLEditor::CreateShadow(nsIDOMNode
* aParentNode
,
207 nsIDOMElement
* aOriginalObject
)
209 // let's create an image through the element factory
211 if (nsHTMLEditUtils::IsImage(aOriginalObject
))
212 name
.AssignLiteral("img");
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
);
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
);
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
253 float resizerWidth
, resizerHeight
;
254 nsCOMPtr
<nsIAtom
> dummyUnit
;
255 mHTMLCSSUtils
->GetComputedProperty(*mTopLeftHandle
, *nsGkAtoms::width
,
257 mHTMLCSSUtils
->ParseLength(value
, &resizerWidth
, getter_AddRefs(dummyUnit
));
258 mHTMLCSSUtils
->GetComputedProperty(*mTopLeftHandle
, *nsGkAtoms::height
,
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
)));
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
)),
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
);
303 nsHTMLEditor::ShowResizers(nsIDOMElement
*aResizedElement
)
305 nsresult res
= ShowResizersInner(aResizedElement
);
312 nsHTMLEditor::ShowResizersInner(nsIDOMElement
*aResizedElement
)
314 NS_ENSURE_ARG_POINTER(aResizedElement
);
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
,
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"));
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
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
,
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;
487 nsHTMLEditor::HideShadowAndInfo()
490 mResizingShadow
->SetAttr(kNameSpaceID_None
, nsGkAtoms::_class
,
491 NS_LITERAL_STRING("hidden"), true);
493 mResizingInfo
->SetAttr(kNameSpaceID_None
, nsGkAtoms::_class
,
494 NS_LITERAL_STRING("hidden"), true);
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
)));
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
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);
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");
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
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();
603 nsHTMLEditor::MouseUp(int32_t aClientX
, int32_t aClientY
,
604 nsIDOMElement
*aTarget
)
607 // we are resizing and release the mouse button, so let's
608 // end the resizing process
611 SetFinalSize(aClientX
, aClientY
);
613 else if (mIsMoving
|| mGrabberClicked
) {
615 mPositioningShadow
->SetAttr(kNameSpaceID_None
, nsGkAtoms::_class
,
616 NS_LITERAL_STRING("hidden"), true);
617 SetFinalPosition(aClientX
, aClientY
);
619 if (mGrabberClicked
) {
628 nsHTMLEditor::SetResizeIncrements(int32_t aX
, int32_t aY
,
629 int32_t aW
, int32_t aH
,
632 mXIncrementFactor
= aX
;
633 mYIncrementFactor
= aY
;
634 mWidthIncrementFactor
= aW
;
635 mHeightIncrementFactor
= aH
;
636 mPreserveRatio
= aPreserveRatio
;
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
)
656 else if (mActivatedHandle
== mTopHandle
||
657 mActivatedHandle
== mBottomHandle
)
658 infoXPosition
= aX
+ (aW
/ 2);
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
)
668 else if (mActivatedHandle
== mLeftHandle
||
669 mActivatedHandle
== mRightHandle
)
670 infoYPosition
= aY
+ (aH
/ 2);
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();
686 mResizingInfo
->RemoveChild(*textInfo
, rv
);
687 NS_ENSURE_TRUE(!rv
.Failed(), rv
.StealNSResult());
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
;
697 diffWidthStr
.Assign('+');
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);
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
,
733 NS_ENSURE_SUCCESS(res
, res
);
739 nsHTMLEditor::GetNewResizingIncrement(int32_t aX
, int32_t aY
, int32_t aID
)
742 if (!mPreserveRatio
) {
746 result
= aX
- mOriginalX
;
750 result
= aY
- mOriginalY
;
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
;
765 result
= (int32_t) (((float) result
) * objectSizeRatio
);
766 result
= (int32_t) (((float) result
) * mWidthIncrementFactor
);
771 result
= (int32_t) (((float) result
) / objectSizeRatio
);
772 result
= (int32_t) (((float) result
) * mHeightIncrementFactor
);
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
);
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
);
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);
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);
816 nsHTMLEditor::MouseMove(nsIDOMEvent
* aMouseEvent
)
818 NS_NAMED_LITERAL_STRING(leftStr
, "left");
819 NS_NAMED_LITERAL_STRING(topStr
, "top");
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
,
836 mHTMLCSSUtils
->SetCSSPropertyPixels(*mResizingShadow
, *nsGkAtoms::top
,
838 mHTMLCSSUtils
->SetCSSPropertyPixels(*mResizingShadow
, *nsGkAtoms::width
,
840 mHTMLCSSUtils
->SetCSSPropertyPixels(*mResizingShadow
, *nsGkAtoms::height
,
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
);
853 LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX
, 1);
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);
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
,
876 mHTMLCSSUtils
->SetCSSPropertyPixels(*mPositioningShadow
, *nsGkAtoms::top
,
883 nsHTMLEditor::SetFinalSize(int32_t aX
, int32_t aY
)
885 if (!mResizedObject
) {
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
);
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
) {
919 mHTMLCSSUtils
->SetCSSPropertyPixels(*resizedObject
, *nsGkAtoms::top
, y
);
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
);
934 mHTMLCSSUtils
->SetCSSPropertyPixels(*resizedObject
, *nsGkAtoms::width
,
937 mHTMLCSSUtils
->SetCSSPropertyPixels(*resizedObject
, *nsGkAtoms::height
,
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
947 mHTMLCSSUtils
->SetCSSPropertyPixels(*resizedObject
, *nsGkAtoms::width
,
950 mHTMLCSSUtils
->SetCSSPropertyPixels(*resizedObject
, *nsGkAtoms::height
,
956 SetAttribute(static_cast<nsIDOMElement
*>(GetAsDOMNode(mResizedObject
)), widthStr
, w
);
961 SetAttribute(static_cast<nsIDOMElement
*>(GetAsDOMNode(mResizedObject
)), heightStr
, h
);
965 mHTMLCSSUtils
->RemoveCSSProperty(*resizedObject
, *nsGkAtoms::width
,
968 mHTMLCSSUtils
->RemoveCSSProperty(*resizedObject
, *nsGkAtoms::height
,
971 // finally notify the listeners if any
972 for (auto& listener
: mObjectResizeEventListeners
) {
973 listener
->OnEndResizing(static_cast<nsIDOMElement
*>(GetAsDOMNode(mResizedObject
)),
974 mResizedObjectWidth
, mResizedObjectHeight
, width
,
978 // keep track of that size
979 mResizedObjectWidth
= width
;
980 mResizedObjectHeight
= height
;
986 nsHTMLEditor::GetResizedObject(nsIDOMElement
* *aResizedObject
)
988 nsCOMPtr
<nsIDOMElement
> ret
= static_cast<nsIDOMElement
*>(GetAsDOMNode(mResizedObject
));
989 ret
.forget(aResizedObject
);
994 nsHTMLEditor::GetObjectResizingEnabled(bool *aIsObjectResizingEnabled
)
996 *aIsObjectResizingEnabled
= mIsObjectResizingEnabled
;
1001 nsHTMLEditor::SetObjectResizingEnabled(bool aObjectResizingEnabled
)
1003 mIsObjectResizingEnabled
= aObjectResizingEnabled
;
1008 nsHTMLEditor::AddObjectResizeEventListener(nsIHTMLObjectResizeListener
* aListener
)
1010 NS_ENSURE_ARG_POINTER(aListener
);
1011 if (mObjectResizeEventListeners
.Contains(aListener
)) {
1012 /* listener already registered */
1014 "trying to register an already registered object resize event listener");
1017 mObjectResizeEventListeners
.AppendElement(*aListener
);
1022 nsHTMLEditor::RemoveObjectResizeEventListener(nsIHTMLObjectResizeListener
* aListener
)
1024 NS_ENSURE_ARG_POINTER(aListener
);
1025 if (!mObjectResizeEventListeners
.Contains(aListener
)) {
1026 /* listener was not registered */
1028 "trying to remove an object resize event listener that was not already registered");
1031 mObjectResizeEventListeners
.RemoveElement(aListener
);