1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Mats Palmgren <mats.palmgren@bredband.net>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 /* code for HTML client-side image maps */
41 #include "nsImageMap.h"
43 #include "nsReadableUtils.h"
44 #include "nsIRenderingContext.h"
45 #include "nsPresContext.h"
48 #include "nsIServiceManager.h"
49 #include "nsNetUtil.h"
50 #include "nsTextFragment.h"
51 #include "nsIContent.h"
52 #include "nsIDOMHTMLElement.h"
53 #include "nsIDOMHTMLMapElement.h"
54 #include "nsIDOMHTMLAreaElement.h"
55 #include "nsIDOMHTMLAnchorElement.h"
56 #include "nsIDOMHTMLCollection.h"
57 #include "nsIDocument.h"
58 #include "nsINameSpaceManager.h"
59 #include "nsGkAtoms.h"
60 #include "nsIDOMEventTarget.h"
61 #include "nsIPresShell.h"
63 #include "nsFrameManager.h"
65 #include "nsIImageMap.h"
66 #include "nsIConsoleService.h"
67 #include "nsIScriptError.h"
68 #include "nsIStringBundle.h"
69 #include "nsIDocument.h"
70 #include "nsContentUtils.h"
72 static NS_DEFINE_CID(kCStringBundleServiceCID
, NS_STRINGBUNDLESERVICE_CID
);
76 Area(nsIContent
* aArea
);
79 virtual void ParseCoords(const nsAString
& aSpec
);
81 virtual PRBool
IsInside(nscoord x
, nscoord y
) const = 0;
82 virtual void Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
) = 0;
83 virtual void GetRect(nsIFrame
* aFrame
, nsRect
& aRect
) = 0;
85 void HasFocus(PRBool aHasFocus
);
87 void GetHREF(nsAString
& aHref
) const;
88 void GetArea(nsIContent
** aArea
) const;
90 nsCOMPtr
<nsIContent
> mArea
;
93 PRPackedBool mHasFocus
;
96 Area::Area(nsIContent
* aArea
)
102 mHasFocus
= PR_FALSE
;
107 MOZ_COUNT_DTOR(Area
);
112 Area::GetHREF(nsAString
& aHref
) const
116 mArea
->GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, aHref
);
121 Area::GetArea(nsIContent
** aArea
) const
124 NS_IF_ADDREF(*aArea
);
140 static void logMessage(nsIContent
* aContent
,
141 const nsAString
& aCoordsSpec
,
143 const char* aMessageName
) {
144 nsIURI
* documentURI
= nsnull
;
145 nsIDocument
* doc
= aContent
->GetOwnerDoc();
147 documentURI
= doc
->GetDocumentURI();
149 nsContentUtils::ReportToConsole(
150 nsContentUtils::eLAYOUT_PROPERTIES
,
153 0, /* params length */
155 PromiseFlatString(NS_LITERAL_STRING("coords=\"") +
157 NS_LITERAL_STRING("\"")), /* source line */
159 0, /* column number */
164 void Area::ParseCoords(const nsAString
& aSpec
)
166 char* cp
= ToNewCString(aSpec
);
174 * Nothing in an empty list
184 * Skip beginning whitespace, all whitespace is empty list.
187 while (is_space(*n_str
))
197 * Make a pass where any two numbers separated by just whitespace
198 * are given a comma separator. Count entries while passing.
201 while (*n_str
!= '\0')
206 * Skip to a separator
209 while (!is_space(*tptr
) && *tptr
!= ',' && *tptr
!= '\0')
216 * If no more entries, break out here
224 * Skip to the end of the separator, noting if we have a
227 has_comma
= PR_FALSE
;
228 while (is_space(*tptr
) || *tptr
== ',')
232 if (has_comma
== PR_FALSE
)
244 * If this was trailing whitespace we skipped, we are done.
246 if ((*tptr
== '\0')&&(has_comma
== PR_FALSE
))
251 * Else if the separator is all whitespace, and this is not the
252 * end of the string, add a comma to the separator.
254 else if (has_comma
== PR_FALSE
)
260 * count the entry skipped.
267 * count the last entry in the list.
272 * Allocate space for the coordinate array.
274 value_list
= new nscoord
[cnt
];
281 * Second pass to copy integer values into list.
284 for (i
=0; i
<cnt
; i
++)
288 ptr
= strchr(tptr
, ',');
294 * Strip whitespace in front of number because I don't
295 * trust atoi to do it on all platforms.
297 while (is_space(*tptr
))
307 value_list
[i
] = (nscoord
) ::atoi(tptr
);
317 mCoords
= value_list
;
323 void Area::HasFocus(PRBool aHasFocus
)
325 mHasFocus
= aHasFocus
;
328 //----------------------------------------------------------------------
330 class DefaultArea
: public Area
{
332 DefaultArea(nsIContent
* aArea
);
334 virtual PRBool
IsInside(nscoord x
, nscoord y
) const;
335 virtual void Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
);
336 virtual void GetRect(nsIFrame
* aFrame
, nsRect
& aRect
);
339 DefaultArea::DefaultArea(nsIContent
* aArea
)
344 PRBool
DefaultArea::IsInside(nscoord x
, nscoord y
) const
349 void DefaultArea::Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
)
352 nsRect r
= aFrame
->GetRect();
356 const nscoord kOnePixel
= nsPresContext::CSSPixelsToAppUnits(1);
357 nscoord x2
= r
.XMost() - kOnePixel
;
358 nscoord y2
= r
.YMost() - kOnePixel
;
359 // XXX aRC.DrawRect(r) result is ugly, that's why we use DrawLine.
360 aRC
.DrawLine(x1
, y1
, x1
, y2
);
361 aRC
.DrawLine(x1
, y2
, x2
, y2
);
362 aRC
.DrawLine(x1
, y1
, x2
, y1
);
363 aRC
.DrawLine(x2
, y1
, x2
, y2
);
367 void DefaultArea::GetRect(nsIFrame
* aFrame
, nsRect
& aRect
)
369 aRect
= aFrame
->GetRect();
373 //----------------------------------------------------------------------
375 class RectArea
: public Area
{
377 RectArea(nsIContent
* aArea
);
379 virtual void ParseCoords(const nsAString
& aSpec
);
380 virtual PRBool
IsInside(nscoord x
, nscoord y
) const;
381 virtual void Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
);
382 virtual void GetRect(nsIFrame
* aFrame
, nsRect
& aRect
);
385 RectArea::RectArea(nsIContent
* aArea
)
390 void RectArea::ParseCoords(const nsAString
& aSpec
)
392 Area::ParseCoords(aSpec
);
394 PRBool saneRect
= PR_TRUE
;
395 PRInt32 flag
= nsIScriptError::warningFlag
;
396 if (mNumCoords
>= 4) {
397 if (mCoords
[0] > mCoords
[2]) {
398 // x-coords in reversed order
399 nscoord x
= mCoords
[2];
400 mCoords
[2] = mCoords
[0];
405 if (mCoords
[1] > mCoords
[3]) {
406 // y-coords in reversed order
407 nscoord y
= mCoords
[3];
408 mCoords
[3] = mCoords
[1];
413 if (mNumCoords
> 4) {
414 // Someone missed the concept of a rect here
419 flag
= nsIScriptError::errorFlag
;
423 logMessage(mArea
, aSpec
, flag
, "ImageMapRectBoundsError");
427 PRBool
RectArea::IsInside(nscoord x
, nscoord y
) const
429 if (mNumCoords
>= 4) { // Note: > is for nav compatability
430 nscoord x1
= mCoords
[0];
431 nscoord y1
= mCoords
[1];
432 nscoord x2
= mCoords
[2];
433 nscoord y2
= mCoords
[3];
434 NS_ASSERTION(x1
<= x2
&& y1
<= y2
,
435 "Someone screwed up RectArea::ParseCoords");
436 if ((x
>= x1
) && (x
<= x2
) && (y
>= y1
) && (y
<= y2
)) {
443 void RectArea::Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
)
446 if (mNumCoords
>= 4) {
447 nscoord x1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
448 nscoord y1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
449 nscoord x2
= nsPresContext::CSSPixelsToAppUnits(mCoords
[2]);
450 nscoord y2
= nsPresContext::CSSPixelsToAppUnits(mCoords
[3]);
451 NS_ASSERTION(x1
<= x2
&& y1
<= y2
,
452 "Someone screwed up RectArea::ParseCoords");
453 aRC
.DrawLine(x1
, y1
, x1
, y2
);
454 aRC
.DrawLine(x1
, y2
, x2
, y2
);
455 aRC
.DrawLine(x1
, y1
, x2
, y1
);
456 aRC
.DrawLine(x2
, y1
, x2
, y2
);
461 void RectArea::GetRect(nsIFrame
* aFrame
, nsRect
& aRect
)
463 if (mNumCoords
>= 4) {
464 nscoord x1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
465 nscoord y1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
466 nscoord x2
= nsPresContext::CSSPixelsToAppUnits(mCoords
[2]);
467 nscoord y2
= nsPresContext::CSSPixelsToAppUnits(mCoords
[3]);
468 NS_ASSERTION(x1
<= x2
&& y1
<= y2
,
469 "Someone screwed up RectArea::ParseCoords");
471 aRect
.SetRect(x1
, y1
, x2
, y2
);
475 //----------------------------------------------------------------------
477 class PolyArea
: public Area
{
479 PolyArea(nsIContent
* aArea
);
481 virtual void ParseCoords(const nsAString
& aSpec
);
482 virtual PRBool
IsInside(nscoord x
, nscoord y
) const;
483 virtual void Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
);
484 virtual void GetRect(nsIFrame
* aFrame
, nsRect
& aRect
);
487 PolyArea::PolyArea(nsIContent
* aArea
)
492 void PolyArea::ParseCoords(const nsAString
& aSpec
)
494 Area::ParseCoords(aSpec
);
496 if (mNumCoords
>= 2) {
497 if (mNumCoords
& 1U) {
500 nsIScriptError::warningFlag
,
501 "ImageMapPolyOddNumberOfCoords");
506 nsIScriptError::errorFlag
,
507 "ImageMapPolyWrongNumberOfCoords");
511 PRBool
PolyArea::IsInside(nscoord x
, nscoord y
) const
513 if (mNumCoords
>= 6) {
514 PRInt32 intersects
= 0;
517 PRInt32 totalv
= mNumCoords
/ 2;
518 PRInt32 totalc
= totalv
* 2;
519 nscoord xval
= mCoords
[totalc
- 2];
520 nscoord yval
= mCoords
[totalc
- 1];
521 PRInt32 end
= totalc
;
524 if ((yval
>= wherey
) != (mCoords
[pointer
] >= wherey
))
525 if ((xval
>= wherex
) == (mCoords
[0] >= wherex
))
526 intersects
+= (xval
>= wherex
) ? 1 : 0;
528 intersects
+= ((xval
- (yval
- wherey
) *
529 (mCoords
[0] - xval
) /
530 (mCoords
[pointer
] - yval
)) >= wherex
) ? 1 : 0;
532 // XXX I wonder what this is doing; this is a translation of ptinpoly.c
533 while (pointer
< end
) {
534 yval
= mCoords
[pointer
];
536 if (yval
>= wherey
) {
537 while((pointer
< end
) && (mCoords
[pointer
] >= wherey
))
541 if ((mCoords
[pointer
-3] >= wherex
) ==
542 (mCoords
[pointer
-1] >= wherex
)) {
543 intersects
+= (mCoords
[pointer
-3] >= wherex
) ? 1 : 0;
546 ((mCoords
[pointer
-3] - (mCoords
[pointer
-2] - wherey
) *
547 (mCoords
[pointer
-1] - mCoords
[pointer
-3]) /
548 (mCoords
[pointer
] - mCoords
[pointer
- 2])) >= wherex
) ? 1:0;
551 while((pointer
< end
) && (mCoords
[pointer
] < wherey
))
555 if ((mCoords
[pointer
-3] >= wherex
) ==
556 (mCoords
[pointer
-1] >= wherex
)) {
557 intersects
+= (mCoords
[pointer
-3] >= wherex
) ? 1:0;
560 ((mCoords
[pointer
-3] - (mCoords
[pointer
-2] - wherey
) *
561 (mCoords
[pointer
-1] - mCoords
[pointer
-3]) /
562 (mCoords
[pointer
] - mCoords
[pointer
- 2])) >= wherex
) ? 1:0;
566 if ((intersects
& 1) != 0) {
573 void PolyArea::Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
)
576 if (mNumCoords
>= 6) {
577 nscoord x0
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
578 nscoord y0
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
580 for (PRInt32 i
= 2; i
< mNumCoords
; i
+= 2) {
581 x1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[i
]);
582 y1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[i
+1]);
583 aRC
.DrawLine(x0
, y0
, x1
, y1
);
587 x1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
588 y1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
589 aRC
.DrawLine(x0
, y0
, x1
, y1
);
594 void PolyArea::GetRect(nsIFrame
* aFrame
, nsRect
& aRect
)
596 if (mNumCoords
>= 6) {
597 nscoord x1
, x2
, y1
, y2
, xtmp
, ytmp
;
598 x1
= x2
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
599 y1
= y2
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
600 for (PRInt32 i
= 2; i
< mNumCoords
; i
+= 2) {
601 xtmp
= nsPresContext::CSSPixelsToAppUnits(mCoords
[i
]);
602 ytmp
= nsPresContext::CSSPixelsToAppUnits(mCoords
[i
+1]);
603 x1
= x1
< xtmp
? x1
: xtmp
;
604 y1
= y1
< ytmp
? y1
: ytmp
;
605 x2
= x2
> xtmp
? x2
: xtmp
;
606 y2
= y2
> ytmp
? y2
: ytmp
;
609 aRect
.SetRect(x1
, y1
, x2
, y2
);
613 //----------------------------------------------------------------------
615 class CircleArea
: public Area
{
617 CircleArea(nsIContent
* aArea
);
619 virtual void ParseCoords(const nsAString
& aSpec
);
620 virtual PRBool
IsInside(nscoord x
, nscoord y
) const;
621 virtual void Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
);
622 virtual void GetRect(nsIFrame
* aFrame
, nsRect
& aRect
);
625 CircleArea::CircleArea(nsIContent
* aArea
)
630 void CircleArea::ParseCoords(const nsAString
& aSpec
)
632 Area::ParseCoords(aSpec
);
634 PRBool wrongNumberOfCoords
= PR_FALSE
;
635 PRInt32 flag
= nsIScriptError::warningFlag
;
636 if (mNumCoords
>= 3) {
637 if (mCoords
[2] < 0) {
640 nsIScriptError::errorFlag
,
641 "ImageMapCircleNegativeRadius");
644 if (mNumCoords
> 3) {
645 wrongNumberOfCoords
= PR_TRUE
;
648 wrongNumberOfCoords
= PR_TRUE
;
649 flag
= nsIScriptError::errorFlag
;
652 if (wrongNumberOfCoords
) {
656 "ImageMapCircleWrongNumberOfCoords");
660 PRBool
CircleArea::IsInside(nscoord x
, nscoord y
) const
662 // Note: > is for nav compatability
663 if (mNumCoords
>= 3) {
664 nscoord x1
= mCoords
[0];
665 nscoord y1
= mCoords
[1];
666 nscoord radius
= mCoords
[2];
672 nscoord dist
= (dx
* dx
) + (dy
* dy
);
673 if (dist
<= (radius
* radius
)) {
680 void CircleArea::Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
)
683 if (mNumCoords
>= 3) {
684 nscoord x1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
685 nscoord y1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
686 nscoord radius
= nsPresContext::CSSPixelsToAppUnits(mCoords
[2]);
690 nscoord x
= x1
- radius
;
691 nscoord y
= y1
- radius
;
692 nscoord w
= 2 * radius
;
693 aRC
.DrawEllipse(x
, y
, w
, w
);
698 void CircleArea::GetRect(nsIFrame
* aFrame
, nsRect
& aRect
)
700 if (mNumCoords
>= 3) {
701 nscoord x1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[0]);
702 nscoord y1
= nsPresContext::CSSPixelsToAppUnits(mCoords
[1]);
703 nscoord radius
= nsPresContext::CSSPixelsToAppUnits(mCoords
[2]);
708 aRect
.SetRect(x1
- radius
, y1
- radius
, x1
+ radius
, y1
+ radius
);
712 //----------------------------------------------------------------------
715 nsImageMap::nsImageMap() :
718 mContainsBlockContents(PR_FALSE
)
722 nsImageMap::~nsImageMap()
724 NS_ASSERTION(mAreas
.Count() == 0, "Destroy was not called");
727 NS_IMPL_ISUPPORTS4(nsImageMap
,
734 nsImageMap::GetBoundsForAreaContent(nsIContent
*aContent
,
735 nsPresContext
* aPresContext
,
738 NS_ENSURE_TRUE(aContent
&& aPresContext
, NS_ERROR_INVALID_ARG
);
740 // Find the Area struct associated with this content node, and return bounds
741 PRInt32 i
, n
= mAreas
.Count();
742 for (i
= 0; i
< n
; i
++) {
743 Area
* area
= (Area
*) mAreas
.ElementAt(i
);
744 if (area
->mArea
== aContent
) {
746 nsIPresShell
* shell
= aPresContext
->PresShell();
748 nsIFrame
* frame
= shell
->GetPrimaryFrameFor(aContent
);
750 area
->GetRect(frame
, aBounds
);
756 return NS_ERROR_FAILURE
;
760 nsImageMap::FreeAreas()
762 nsFrameManager
*frameManager
= mPresShell
->FrameManager();
764 PRInt32 i
, n
= mAreas
.Count();
765 for (i
= 0; i
< n
; i
++) {
766 Area
* area
= (Area
*) mAreas
.ElementAt(i
);
767 frameManager
->RemoveAsPrimaryFrame(area
->mArea
, mImageFrame
);
769 nsCOMPtr
<nsIContent
> areaContent
;
770 area
->GetArea(getter_AddRefs(areaContent
));
772 areaContent
->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMFocusListener
));
780 nsImageMap::Init(nsIPresShell
* aPresShell
, nsIFrame
* aImageFrame
, nsIDOMHTMLMapElement
* aMap
)
782 NS_PRECONDITION(nsnull
!= aMap
, "null ptr");
783 if (nsnull
== aMap
) {
784 return NS_ERROR_NULL_POINTER
;
786 mPresShell
= aPresShell
;
787 mImageFrame
= aImageFrame
;
789 mMap
= do_QueryInterface(aMap
);
790 NS_ASSERTION(mMap
, "aMap is not an nsIContent!");
791 mMap
->AddMutationObserver(this);
793 // "Compile" the areas in the map into faster access versions
794 return UpdateAreas();
799 nsImageMap::SearchForAreas(nsIContent
* aParent
, PRBool
& aFoundArea
,
800 PRBool
& aFoundAnchor
)
803 PRUint32 i
, n
= aParent
->GetChildCount();
805 // Look for <area> or <a> elements. We'll use whichever type we find first.
806 for (i
= 0; i
< n
; i
++) {
807 nsIContent
*child
= aParent
->GetChildAt(i
);
809 if (child
->IsNodeOfType(nsINode::eHTML
)) {
810 // If we haven't determined that the map element contains an
811 // <a> element yet, then look for <area>.
812 if (!aFoundAnchor
&& child
->Tag() == nsGkAtoms::area
) {
813 aFoundArea
= PR_TRUE
;
815 NS_ENSURE_SUCCESS(rv
, rv
);
817 // Continue to next child. This stops mContainsBlockContents from
818 // getting set. It also makes us ignore children of <area>s which
819 // is consistent with how we react to dynamic insertion of such
823 // If we haven't determined that the map element contains an
824 // <area> element yet, then look for <a>.
825 if (!aFoundArea
&& child
->Tag() == nsGkAtoms::a
) {
826 aFoundAnchor
= PR_TRUE
;
828 NS_ENSURE_SUCCESS(rv
, rv
);
832 if (child
->IsNodeOfType(nsINode::eELEMENT
)) {
833 mContainsBlockContents
= PR_TRUE
;
834 rv
= SearchForAreas(child
, aFoundArea
, aFoundAnchor
);
835 NS_ENSURE_SUCCESS(rv
, rv
);
843 nsImageMap::UpdateAreas()
845 // Get rid of old area data
848 PRBool foundArea
= PR_FALSE
;
849 PRBool foundAnchor
= PR_FALSE
;
850 mContainsBlockContents
= PR_FALSE
;
852 return SearchForAreas(mMap
, foundArea
, foundAnchor
);
856 nsImageMap::AddArea(nsIContent
* aArea
)
859 static nsIContent::AttrValuesArray strings
[] =
860 {&nsGkAtoms::_empty
, &nsGkAtoms::rect
, &nsGkAtoms::rectangle
,
861 &nsGkAtoms::poly
, &nsGkAtoms::polygon
, &nsGkAtoms::circle
,
862 &nsGkAtoms::circ
, &nsGkAtoms::_default
, nsnull
};
864 aArea
->GetAttr(kNameSpaceID_None
, nsGkAtoms::coords
, coords
);
867 switch (aArea
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::shape
,
868 strings
, eIgnoreCase
)) {
869 case nsIContent::ATTR_MISSING
:
873 area
= new RectArea(aArea
);
877 area
= new PolyArea(aArea
);
881 area
= new CircleArea(aArea
);
884 area
= new DefaultArea(aArea
);
887 // Unknown area type; bail
891 return NS_ERROR_OUT_OF_MEMORY
;
893 //Add focus listener to track area focus changes
894 aArea
->AddEventListenerByIID(this, NS_GET_IID(nsIDOMFocusListener
));
896 mPresShell
->FrameManager()->SetPrimaryFrameFor(aArea
, mImageFrame
);
897 aArea
->SetMayHaveFrame(PR_TRUE
);
898 NS_ASSERTION(aArea
->MayHaveFrame(), "SetMayHaveFrame failed?");
900 area
->ParseCoords(coords
);
901 mAreas
.AppendElement(area
);
906 nsImageMap::IsInside(nscoord aX
, nscoord aY
,
907 nsIContent
** aContent
) const
909 NS_ASSERTION(mMap
, "Not initialized");
910 PRInt32 i
, n
= mAreas
.Count();
911 for (i
= 0; i
< n
; i
++) {
912 Area
* area
= (Area
*) mAreas
.ElementAt(i
);
913 if (area
->IsInside(aX
, aY
)) {
914 area
->GetArea(aContent
);
924 nsImageMap::Draw(nsIFrame
* aFrame
, nsIRenderingContext
& aRC
)
926 PRInt32 i
, n
= mAreas
.Count();
927 for (i
= 0; i
< n
; i
++) {
928 Area
* area
= (Area
*) mAreas
.ElementAt(i
);
929 area
->Draw(aFrame
, aRC
);
934 nsImageMap::MaybeUpdateAreas(nsIContent
*aContent
)
936 if (aContent
== mMap
|| mContainsBlockContents
) {
942 nsImageMap::AttributeChanged(nsIDocument
* aDocument
,
943 nsIContent
* aContent
,
944 PRInt32 aNameSpaceID
,
949 // If the parent of the changing content node is our map then update
950 // the map. But only do this if the node is an HTML <area> or <a>
951 // and the attribute that's changing is "shape" or "coords" -- those
952 // are the only cases we care about.
953 if ((aContent
->NodeInfo()->Equals(nsGkAtoms::area
) ||
954 aContent
->NodeInfo()->Equals(nsGkAtoms::a
)) &&
955 aContent
->IsNodeOfType(nsINode::eHTML
) &&
956 aNameSpaceID
== kNameSpaceID_None
&&
957 (aAttribute
== nsGkAtoms::shape
||
958 aAttribute
== nsGkAtoms::coords
)) {
959 MaybeUpdateAreas(aContent
->GetParent());
964 nsImageMap::ContentAppended(nsIDocument
*aDocument
,
965 nsIContent
* aContainer
,
966 PRInt32 aNewIndexInContainer
)
968 MaybeUpdateAreas(aContainer
);
972 nsImageMap::ContentInserted(nsIDocument
*aDocument
,
973 nsIContent
* aContainer
,
975 PRInt32 aIndexInContainer
)
977 MaybeUpdateAreas(aContainer
);
981 nsImageMap::ContentRemoved(nsIDocument
*aDocument
,
982 nsIContent
* aContainer
,
984 PRInt32 aIndexInContainer
)
986 MaybeUpdateAreas(aContainer
);
990 nsImageMap::Focus(nsIDOMEvent
* aEvent
)
992 return ChangeFocus(aEvent
, PR_TRUE
);
996 nsImageMap::Blur(nsIDOMEvent
* aEvent
)
998 return ChangeFocus(aEvent
, PR_FALSE
);
1002 nsImageMap::ChangeFocus(nsIDOMEvent
* aEvent
, PRBool aFocus
)
1004 //Set which one of our areas changed focus
1005 nsCOMPtr
<nsIDOMEventTarget
> target
;
1006 if (NS_SUCCEEDED(aEvent
->GetTarget(getter_AddRefs(target
))) && target
) {
1007 nsCOMPtr
<nsIContent
> targetContent(do_QueryInterface(target
));
1008 if (targetContent
) {
1009 PRInt32 i
, n
= mAreas
.Count();
1010 for (i
= 0; i
< n
; i
++) {
1011 Area
* area
= (Area
*) mAreas
.ElementAt(i
);
1012 nsCOMPtr
<nsIContent
> areaContent
;
1013 area
->GetArea(getter_AddRefs(areaContent
));
1014 if (areaContent
.get() == targetContent
.get()) {
1015 //Set or Remove internal focus
1016 area
->HasFocus(aFocus
);
1017 //Now invalidate the rect
1018 nsCOMPtr
<nsIDocument
> doc
= targetContent
->GetDocument();
1019 //This check is necessary to see if we're still attached to the doc
1021 nsIPresShell
*presShell
= doc
->GetPrimaryShell();
1023 nsIFrame
* imgFrame
= presShell
->GetPrimaryFrameFor(targetContent
);
1026 area
->GetRect(imgFrame
, dmgRect
);
1027 imgFrame
->Invalidate(dmgRect
);
1040 nsImageMap::HandleEvent(nsIDOMEvent
* aEvent
)
1046 nsImageMap::Destroy(void)
1049 mMap
->RemoveMutationObserver(this);